diff --git a/CHANGELOG.md b/CHANGELOG.md index d41a121..8894b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + + ## [Unreleased] ### Added +- Add compatibility for JSR experiment type - Add codemeta file ### Changed diff --git a/pyked/chemked.py b/pyked/chemked.py index fdd147e..05fb95a 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -2,6 +2,7 @@ Main ChemKED module """ # Standard libraries +import os from os.path import exists from collections import namedtuple from warnings import warn @@ -11,6 +12,7 @@ from itertools import chain import numpy as np +import pandas as pd # Local imports from .validation import schema, OurValidator, yaml, Q_ @@ -120,8 +122,15 @@ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): self.validate_yaml(self._properties) self.datapoints = [] - for point in self._properties['datapoints']: - self.datapoints.append(DataPoint(point)) + if self._properties['experiment-type'] == 'ignition delay': + for point in self._properties['datapoints']: + self.datapoints.append(IgnitionDataPoint(point)) + elif self._properties['experiment-type'] == 'species profile': + for point in self._properties['datapoints']: + csv_file = os.path.join(os.path.split(yaml_file)[0], point['csvfile']) + csv_df = pd.read_csv(csv_file) + for i in range(0, len(csv_df)): + self.datapoints.append(SpeciesProfileDataPoint(point, csv_df, i)) self.reference = Reference( volume=self._properties['reference'].get('volume'), @@ -374,10 +383,11 @@ def convert_to_ReSpecTh(self, filename): citation = '' for author in self.reference.authors: citation += author['name'] + ', ' - citation += (self.reference.journal + ' (' + str(self.reference.year) + ') ' + - str(self.reference.volume) + ':' + self.reference.pages + '. ' + - self.reference.detail - ) + citation += ( + self.reference.journal + ' (' + str(self.reference.year) + ') ' + + str(self.reference.volume) + ':' + self.reference.pages + '. ' + + self.reference.detail + ) reference.set('preferredKey', citation) reference.set('doi', self.reference.doi) @@ -443,17 +453,21 @@ def convert_to_ReSpecTh(self, filename): datagroup_link.set('dataPointID', '') property_idx = {} - labels = {'temperature': 'T', 'pressure': 'P', - 'ignition delay': 'tau', 'pressure rise': 'dP/dt', - } + labels = { + 'temperature': 'T', + 'pressure': 'P', + 'ignition delay': 'tau', + 'pressure rise': 'dP/dt', + } for prop_name in datagroup_properties: attribute = prop_name.replace(' ', '_') # This can't be hasattr because properties are set to the value None # if no value is specified in the file, so the attribute always exists - prop_indices = [i for i, dp in enumerate(self.datapoints) - if getattr(dp, attribute) is not None - ] + prop_indices = [ + i for i, dp in enumerate(self.datapoints) + if getattr(dp, attribute) is not None + ] if prop_name in common or not prop_indices: continue @@ -472,9 +486,9 @@ def convert_to_ReSpecTh(self, filename): for dp in self.datapoints: for species in dp.composition.values(): # Only add new property for species not already considered - has_spec = any([species.species_name in d.values() - for d in property_idx.values() - ]) + has_spec = any( + [species.species_name in d.values() for d in property_idx.values()] + ) if not has_spec: prop = etree.SubElement(datagroup, 'property') prop.set('description', '') @@ -507,9 +521,15 @@ def convert_to_ReSpecTh(self, filename): value.text = str(item.amount.magnitude) # See https://stackoverflow.com/a/16097112 for the None.__ne__ - history_types = ['volume_history', 'temperature_history', 'pressure_history', - 'piston_position_history', 'light_emission_history', - 'OH_emission_history', 'absorption_history'] + history_types = [ + 'volume_history', + 'temperature_history', + 'pressure_history', + 'piston_position_history', + 'light_emission_history', + 'OH_emission_history', + 'absorption_history' + ] time_histories = [getattr(dp, p) for dp in self.datapoints for p in history_types] time_histories = list(filter(None.__ne__, time_histories)) @@ -570,8 +590,10 @@ def convert_to_ReSpecTh(self, filename): else: ignition.set('type', self.datapoints[0].ignition_type['type']) else: - raise NotImplementedError('Different ignition targets or types for multiple datapoints ' - 'are not supported in ReSpecTh.') + raise NotImplementedError( + 'Different ignition targets or types for multiple datapoints ' + 'are not supported in ReSpecTh.' + ) et = etree.ElementTree(root) et.write(filename, encoding='utf-8', xml_declaration=True) @@ -586,10 +608,181 @@ def convert_to_ReSpecTh(self, filename): class DataPoint(object): - """Class for a single datapoint. + """ + A base class for a single datapoint. - The `DataPoint` class stores the information associated with a single data point in the dataset - parsed from the `ChemKED` YAML input. + Specific types of data point should inherit from this. + """ + def process_csv_row(self, csv_df, row, properties=None, outlet_comp=False, species_name=None): + """ + Process a single entry in column data and return as a units.Quantity object + csv_df is a Pandas DataFrame. + """ + if not outlet_comp: + column_name = properties[0]['column-name'] + value = csv_df[column_name][row] + for p in properties: + units = p.get('units', '') + if units: + break + # TODO: schema should enforce at most 1 units entry + + value_properties = [f'{value} {units}'] + + for p in properties: + if p.get('uncertainty-type', False): + # this is the uncertainty data + value_properties.append(p) + + data_entry = self.process_quantity(value_properties) + elif outlet_comp and species_name: + species_amount = csv_df[species_name][row] + column_property = [f'{species_amount}'] + data_entry = self.process_quantity(column_property) + return data_entry + + def process_quantity(self, properties): + """ + Process the units and uncertainty information from a given quantity + and return it as a units.Quantity object. + """ + quant = Q_(properties[0]) + if len(properties) > 1: + unc = properties[1] + uncertainty = unc.get('uncertainty', False) + upper_uncertainty = unc.get('upper-uncertainty', False) + lower_uncertainty = unc.get('lower-uncertainty', False) + uncertainty_type = unc.get('uncertainty-type') + if uncertainty_type == 'relative': + if uncertainty: + quant = quant.plus_minus(float(uncertainty), relative=True) + elif upper_uncertainty and lower_uncertainty: + warn( + 'Asymmetric uncertainties are not supported. The ' + 'maximum of lower-uncertainty and upper-uncertainty ' + 'has been used as the symmetric uncertainty.' + ) + uncertainty = max(float(upper_uncertainty), float(lower_uncertainty)) + quant = quant.plus_minus(uncertainty, relative=True) + else: + raise ValueError( + 'Either "uncertainty" or "upper-uncertainty" and ' + '"lower-uncertainty" need to be specified.' + ) + elif uncertainty_type == 'absolute': + if uncertainty: + uncertainty = Q_(uncertainty) + quant = quant.plus_minus(uncertainty.to(quant.units).magnitude) + elif upper_uncertainty and lower_uncertainty: + warn( + 'Asymmetric uncertainties are not supported. The ' + 'maximum of lower-uncertainty and upper-uncertainty ' + 'has been used as the symmetric uncertainty.' + ) + uncertainty = max(Q_(upper_uncertainty), Q_(lower_uncertainty)) + quant = quant.plus_minus(uncertainty.to(quant.units).magnitude) + else: + raise ValueError( + 'Either "uncertainty" or "upper-uncertainty" and ' + '"lower-uncertainty" need to be specified.' + ) + else: + raise ValueError('uncertainty-type must be one of "absolute" or "relative"') + return quant + + def species_in_datapoint(self, species): + raise NotImplementedError + + +class SpeciesProfileDataPoint(DataPoint): + """ + Class for a single JSR experiment data point. + + The `SpeciesProfileDataPoint` class stores the information associated with + a species concentration profile for one set of reactor conditions in the + dataset parsed from the `ChemKED` YAML input. + + Arguments: + properties (`dict`): Dictionary adhering to the ChemKED format for ``datapoints`` + + Attributes: + inlet_composition (`dict`): Dictionary representing the species and their quantities + outet_composition (`dict`): Dictionary representing the species and their quantities + temperature (pint.Quantity): The temperature of the experiment + pressure (pint.Quantity): The pressure of the experiment + reactor_volume (pint.Quantity): The volume of the reactor + residence_time (pint.Quantity): Reactor volume divided by the volumetric flow rate + """ + value_unit_props = [ + 'pressure', 'reactor-volume', 'residence-time' + ] + + column_unit_props = [ + 'temperature', + ] + + def __init__(self, properties, csv_df, row): + for prop in self.value_unit_props: + if prop in properties: + quant = self.process_quantity(properties[prop]) + setattr(self, prop.replace('-', '_'), quant) + else: + setattr(self, prop.replace('-', '_'), None) + + for prop in self.column_unit_props: + if prop in properties: + data_entry = self.process_csv_row(properties=properties[prop], csv_df=csv_df, row=row) + setattr(self, prop.replace('-', '_'), data_entry) + else: + setattr(self, prop.replace('-', '_'), None) + + self.inlet_composition_type = properties['inlet-composition']['kind'] + inlet_composition = {} + for species in properties['inlet-composition']['species']: + species_name = species['species-name'] + amount = self.process_quantity(species['amount']) + InChI = species.get('InChI') + SMILES = species.get('SMILES') + atomic_composition = species.get('atomic-composition') + inlet_composition[species_name] = Composition( + species_name=species_name, + InChI=InChI, SMILES=SMILES, + atomic_composition=atomic_composition, + amount=amount + ) + + setattr(self, 'inlet_composition', inlet_composition) + + self.outlet_composition_type = properties['outlet-composition']['kind'] + outlet_composition = {} + for species in properties['outlet-composition']['species']: + species_name = species['species-name'] + amount = self.process_csv_row(csv_df=csv_df, row=row, species_name=species_name, outlet_comp=True) + InChI = species.get('InChI') + SMILES = species.get('SMILES') + atomic_composition = species.get('atomic-composition') + outlet_composition[species_name] = Composition( + species_name=species_name, + InChI=InChI, + SMILES=SMILES, + atomic_composition=atomic_composition, + amount=amount + ) + + setattr(self, 'outlet_composition', outlet_composition) + + def species_in_datapoint(self, species): + return ( + species in self.outlet_composition.keys() + or species in self.inlet_composition.keys() + ) + + +class IgnitionDataPoint(DataPoint): + """Class for a single ignition delay datapoint. + + The `IgnitionDataPoint` class stores the information associated with a single ignition data point + in the dataset parsed from the `ChemKED` YAML input. Arguments: properties (`dict`): Dictionary adhering to the ChemKED format for ``datapoints`` @@ -665,8 +858,12 @@ def __init__(self, properties): SMILES = species.get('SMILES') atomic_composition = species.get('atomic-composition') composition[species_name] = Composition( - species_name=species_name, InChI=InChI, SMILES=SMILES, - atomic_composition=atomic_composition, amount=amount) + species_name=species_name, + InChI=InChI, + SMILES=SMILES, + atomic_composition=atomic_composition, + amount=amount + ) setattr(self, 'composition', composition) @@ -679,8 +876,10 @@ def __init__(self, properties): if 'time-histories' in properties: for hist in properties['time-histories']: if hasattr(self, '{}_history'.format(hist['type'].replace(' ', '_'))): - raise ValueError('Each history type may only be specified once. {} was ' - 'specified multiple times'.format(hist['type'])) + raise ValueError( + 'Each history type may only be specified once. {} was ' + 'specified multiple times'.format(hist['type']) + ) time_col = hist['time']['column'] time_units = hist['time']['units'] quant_col = hist['quantity']['column'] @@ -700,9 +899,11 @@ def __init__(self, properties): setattr(self, '{}_history'.format(hist['type'].replace(' ', '_')), time_history) if 'volume-history' in properties: - warn('The volume-history field should be replaced by time-histories. ' - 'volume-history will be removed after PyKED 0.4', - DeprecationWarning) + warn( + 'The volume-history field should be replaced by time-histories. ' + 'volume-history will be removed after PyKED 0.4', + DeprecationWarning + ) time_col = properties['volume-history']['time']['column'] time_units = properties['volume-history']['time']['units'] volume_col = properties['volume-history']['volume']['column'] @@ -713,51 +914,21 @@ def __init__(self, properties): volume=Q_(values[:, volume_col], volume_units), ) - history_types = ['volume', 'temperature', 'pressure', 'piston_position', 'light_emission', - 'OH_emission', 'absorption'] + history_types = [ + 'volume', + 'temperature', + 'pressure', + 'piston_position', + 'light_emission', + 'OH_emission', + 'absorption' + ] for h in history_types: if not hasattr(self, '{}_history'.format(h)): setattr(self, '{}_history'.format(h), None) - def process_quantity(self, properties): - """Process the uncertainty information from a given quantity and return it - """ - quant = Q_(properties[0]) - if len(properties) > 1: - unc = properties[1] - uncertainty = unc.get('uncertainty', False) - upper_uncertainty = unc.get('upper-uncertainty', False) - lower_uncertainty = unc.get('lower-uncertainty', False) - uncertainty_type = unc.get('uncertainty-type') - if uncertainty_type == 'relative': - if uncertainty: - quant = quant.plus_minus(float(uncertainty), relative=True) - elif upper_uncertainty and lower_uncertainty: - warn('Asymmetric uncertainties are not supported. The ' - 'maximum of lower-uncertainty and upper-uncertainty ' - 'has been used as the symmetric uncertainty.') - uncertainty = max(float(upper_uncertainty), float(lower_uncertainty)) - quant = quant.plus_minus(uncertainty, relative=True) - else: - raise ValueError('Either "uncertainty" or "upper-uncertainty" and ' - '"lower-uncertainty" need to be specified.') - elif uncertainty_type == 'absolute': - if uncertainty: - uncertainty = Q_(uncertainty) - quant = quant.plus_minus(uncertainty.to(quant.units).magnitude) - elif upper_uncertainty and lower_uncertainty: - warn('Asymmetric uncertainties are not supported. The ' - 'maximum of lower-uncertainty and upper-uncertainty ' - 'has been used as the symmetric uncertainty.') - uncertainty = max(Q_(upper_uncertainty), Q_(lower_uncertainty)) - quant = quant.plus_minus(uncertainty.to(quant.units).magnitude) - else: - raise ValueError('Either "uncertainty" or "upper-uncertainty" and ' - '"lower-uncertainty" need to be specified.') - else: - raise ValueError('uncertainty-type must be one of "absolute" or "relative"') - - return quant + def species_in_datapoint(self, species): + return species in self.composition.keys() def get_cantera_composition_string(self, species_conversion=None): """Get the composition in a string format suitable for input to Cantera. @@ -791,19 +962,22 @@ def get_cantera_composition_string(self, species_conversion=None): if species_conversion is None: comps = ['{!s}:{:.4e}'.format(c.species_name, - c.amount.magnitude/factor) for c in self.composition.values()] + c.amount.magnitude / factor) for c in self.composition.values()] else: comps = [] for c in self.composition.values(): - amount = c.amount.magnitude/factor + amount = c.amount.magnitude / factor idents = [getattr(c, s, False) for s in ['species_name', 'InChI', 'SMILES']] present = [i in species_conversion for i in idents] if not any(present): comps.append('{!s}:{:.4e}'.format(c.species_name, amount)) else: if len([i for i in present if i]) > 1: - raise ValueError('More than one conversion present for species {}'.format( - c.species_name)) + raise ValueError( + 'More than one conversion present for species {}'.format( + c.species_name + ) + ) ident = idents[present.index(True)] species_replacement_name = species_conversion.pop(ident) diff --git a/pyked/schemas/chemked_schema.yaml b/pyked/schemas/chemked_schema.yaml index 3592089..fb80fdf 100644 --- a/pyked/schemas/chemked_schema.yaml +++ b/pyked/schemas/chemked_schema.yaml @@ -29,6 +29,8 @@ common-properties: ignition-type: *ignition-type composition: *composition pressure-rise: *value-unit-optional + reactor-volume: *value-unit-optional + residence-time: *value-unit-optional apparatus: required: true @@ -38,6 +40,7 @@ apparatus: allowed: - shock tube - rapid compression machine + - jet stirred reactor required: true type: string institution: @@ -73,7 +76,7 @@ reference: type: integer pages: type: string -chemked-version: # TODO: Implement proper version comparison +chemked-version: allowed: - 0.0.1 - 0.1.0 @@ -93,6 +96,7 @@ chemked-version: # TODO: Implement proper version comparison experiment-type: allowed: - ignition delay + - species profile required: true type: string file-authors: diff --git a/pyked/schemas/jsr_schema.yaml b/pyked/schemas/jsr_schema.yaml new file mode 100644 index 0000000..dbc6f96 --- /dev/null +++ b/pyked/schemas/jsr_schema.yaml @@ -0,0 +1,7 @@ +# Common reference for jet stirred reactors +jsr-schema: &jsr-schema + type: list + schema: + type: dict + schema: + inlet: *composition \ No newline at end of file diff --git a/pyked/schemas/species_profile_schema.yaml b/pyked/schemas/species_profile_schema.yaml new file mode 100644 index 0000000..2d07941 --- /dev/null +++ b/pyked/schemas/species_profile_schema.yaml @@ -0,0 +1,40 @@ +# Common reference for species profile schema +species-profile-schema: &species-profile-schema + type: list + minlength: 1 + schema: + type: dict + schema: + csv-file: + required: true + type: string + temperature: + required: true + type: dict + schema: + column-name: + required: true + type: string + units: + required: true + type: string + uncertainty: + type: dict + schema: + type: + required: true + type: string + allowed: + - relative + - absolute + value: + required: true + type: string + pressure: *value-unit-required + reactor-volume: *value-unit-required + residence-time: *value-unit-required + equivalence-ratio: + type: float + min: 0.0 + inlet-composition: *composition + outlet-composition: *composition \ No newline at end of file diff --git a/pyked/tests/new_zhang_phi0.25.csv b/pyked/tests/new_zhang_phi0.25.csv new file mode 100644 index 0000000..338fc68 --- /dev/null +++ b/pyked/tests/new_zhang_phi0.25.csv @@ -0,0 +1,24 @@ +Temperature,n-heptane,oxygen,carbon monoxide,carbon dioxide,methane,acetylene,ethylene,ethane,propene,propane,propadiene,propyne,formaldehyde,ethylene oxide,acetaldehyde,1-butene,2-butene,"1,3-butadiene",n-butane,ethanol,propylene oxide,furan,acrolein,propanal,acetone,1-pentene,2-pentene,cyclopentene,"1,3-pentadiene",2-propen-1-ol,propan-1-ol,butenone,methyl-furan,butanal,2-butanone,1-hexene,2-butenal,acetic acid,pentanal + pentanone,benzene,dihydrofuran,3-heptene,2-heptene,1-heptene,2-hexanone,propanoic acid,2-ethyl-5-methyl-furan,2-methyl-dihydrofuranone,sum of cyclic ether with 5 atoms,sum of cyclic ether with 4 atoms,sum of cyclic ether with 3 atoms,2methanol-5methyl-tetrahydrofuranone,heptanone +500,4.91E-03,2.33E-01,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,3.40E-08,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,1.45E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,2.50E-08,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,1.40E-06,0.00E+00,3.10E-07,0.00E+00,0.00E+00 +525,4.77E-03,2.30E-01,0.00E+00,0.00E+00,0.00E+00,0.00E+00,1.35E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,2.86E-05,0.00E+00,0.00E+00,0.00E+00,0.00E+00,1.49E-06,0.00E+00,0.00E+00,0.00E+00,5.45E-05,4.60E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,7.49E-07,0.00E+00,4.10E-06,8.55E-06,0.00E+00,0.00E+00,7.65E-06,1.45E-06,2.50E-08,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,9.89E-06,1.21E-06,0.00E+00,3.81E-06,0.00E+00,6.40E-07,0.00E+00,0.00E+00 +550,3.78E-03,2.29E-01,9.46E-05,0.00E+00,0.00E+00,0.00E+00,1.28E-05,0.00E+00,4.39E-06,0.00E+00,0.00E+00,0.00E+00,1.92E-05,4.25E-06,1.90E-04,1.21E-06,0.00E+00,0.00E+00,0.00E+00,6.32E-06,3.27E-06,0.00E+00,1.89E-06,2.91E-04,6.10E-05,7.09E-07,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,2.70E-06,1.18E-06,1.69E-05,7.49E-05,0.00E+00,1.30E-06,1.42E-04,4.38E-05,2.50E-08,5.98E-07,3.13E-06,1.09E-06,0.00E+00,3.76E-06,4.15E-05,1.01E-05,7.24E-06,5.88E-06,8.43E-06,5.78E-06,0.00E+00,0.00E+00 +575,3.24E-03,2.20E-01,1.03E-03,6.95E-04,0.00E+00,0.00E+00,4.29E-05,0.00E+00,1.55E-05,0.00E+00,0.00E+00,0.00E+00,1.35E-04,1.65E-05,4.92E-04,4.04E-06,0.00E+00,0.00E+00,0.00E+00,1.61E-05,8.05E-06,1.09E-06,1.71E-05,7.59E-04,1.04E-04,1.42E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,1.29E-05,4.19E-06,3.62E-05,1.09E-04,2.79E-07,4.64E-06,1.57E-04,6.88E-05,2.50E-08,2.49E-06,8.56E-06,3.97E-06,1.23E-06,3.56E-05,1.09E-04,1.75E-05,1.21E-05,8.65E-05,4.60E-05,1.38E-05,7.56E-07,0.00E+00 +600,2.34E-03,2.15E-01,3.51E-03,1.67E-03,1.31E-06,0.00E+00,1.02E-04,6.92E-07,3.23E-05,0.00E+00,0.00E+00,0.00E+00,1.20E-03,4.25E-05,6.75E-04,8.07E-06,0.00E+00,4.21E-07,0.00E+00,2.34E-05,1.36E-05,2.03E-06,4.50E-05,9.81E-04,9.70E-05,2.75E-06,0.00E+00,0.00E+00,0.00E+00,8.81E-07,0.00E+00,3.58E-05,4.95E-06,5.45E-05,7.64E-05,6.28E-07,9.13E-06,1.46E-04,4.89E-05,2.50E-08,3.74E-06,7.31E-06,3.97E-06,4.18E-06,1.65E-05,5.54E-05,1.50E-05,8.69E-06,9.44E-05,7.81E-05,1.99E-05,1.61E-06,2.41E-06 +625,2.02E-03,2.06E-01,6.17E-03,2.21E-03,1.75E-06,1.27E-07,1.88E-04,6.92E-07,8.62E-05,0.00E+00,0.00E+00,0.00E+00,1.52E-03,5.31E-05,8.01E-04,1.21E-05,4.04E-07,8.84E-07,3.62E-07,1.35E-05,1.46E-05,2.92E-06,5.58E-05,1.05E-03,5.69E-05,4.43E-06,0.00E+00,0.00E+00,0.00E+00,2.27E-06,0.00E+00,4.92E-05,5.71E-06,6.03E-05,3.62E-05,8.84E-07,1.07E-05,7.29E-05,2.75E-05,2.50E-08,9.22E-06,1.59E-05,5.85E-06,1.86E-06,3.76E-06,1.98E-05,0.00E+00,3.09E-06,2.23E-04,1.49E-04,3.47E-05,2.30E-06,1.59E-05 +650,1.79E-03,2.10E-01,7.05E-03,2.02E-03,3.94E-06,9.76E-08,3.61E-04,6.92E-07,1.03E-04,0.00E+00,0.00E+00,0.00E+00,1.68E-03,5.43E-05,8.24E-04,2.42E-05,6.06E-07,2.10E-06,7.24E-07,1.09E-05,1.56E-05,3.26E-06,8.46E-05,1.14E-03,6.05E-05,9.16E-06,0.00E+00,0.00E+00,1.33E-06,4.41E-06,0.00E+00,6.75E-05,6.86E-06,7.43E-05,3.27E-05,1.49E-06,1.51E-05,4.01E-05,2.17E-05,2.50E-08,1.52E-05,2.86E-05,1.38E-05,5.01E-06,2.43E-06,1.30E-05,0.00E+00,0.00E+00,3.16E-04,2.13E-04,5.39E-05,3.91E-06,2.07E-05 +675,1.65E-03,2.13E-01,6.52E-03,1.39E-03,6.56E-06,0.00E+00,5.42E-04,1.38E-06,1.76E-04,0.00E+00,0.00E+00,0.00E+00,1.42E-03,5.20E-05,7.89E-04,4.84E-05,8.07E-07,4.21E-06,1.09E-06,6.84E-06,1.61E-05,3.71E-06,1.22E-04,1.33E-03,6.60E-05,2.01E-05,7.09E-07,7.68E-07,1.72E-06,8.18E-06,1.03E-06,7.38E-05,8.76E-06,9.12E-05,3.37E-05,2.74E-06,2.36E-05,2.19E-05,2.50E-05,2.50E-08,1.44E-05,4.68E-05,2.15E-05,8.98E-06,2.89E-06,1.29E-05,0.00E+00,0.00E+00,4.49E-04,3.14E-04,7.32E-05,7.19E-06,4.34E-05 +700,1.64E-03,2.14E-01,3.68E-03,9.37E-04,1.46E-05,0.00E+00,5.63E-04,2.49E-06,2.58E-04,6.45E-07,0.00E+00,0.00E+00,1.30E-03,3.19E-05,7.09E-04,8.88E-05,1.21E-06,9.26E-06,1.74E-06,4.69E-06,1.56E-05,5.44E-06,1.44E-04,1.47E-03,6.10E-05,4.43E-05,1.36E-06,1.89E-06,5.34E-06,8.81E-06,1.21E-06,5.62E-05,1.18E-05,1.08E-04,3.62E-05,6.91E-06,3.14E-05,1.09E-05,2.64E-05,2.50E-08,1.20E-05,7.37E-05,3.72E-05,1.38E-05,3.76E-06,1.29E-05,0.00E+00,0.00E+00,5.27E-04,3.85E-04,9.77E-05,1.02E-05,5.66E-05 +725,1.94E-03,2.18E-01,2.84E-03,6.32E-04,1.97E-05,2.46E-07,6.19E-04,2.84E-06,3.36E-04,7.38E-07,0.00E+00,4.56E-07,1.18E-03,2.83E-05,6.86E-04,1.33E-04,1.61E-06,1.68E-05,2.53E-06,3.42E-06,1.56E-05,6.43E-06,1.69E-04,1.53E-03,6.35E-05,7.98E-05,1.77E-06,2.19E-06,9.65E-06,1.07E-05,1.33E-06,4.79E-05,1.56E-05,1.18E-04,4.57E-05,1.27E-05,3.54E-05,0.00E+00,2.79E-05,3.14E-07,1.10E-05,1.14E-04,5.95E-05,2.02E-05,4.91E-06,1.09E-05,0.00E+00,0.00E+00,5.84E-04,5.13E-04,1.06E-04,1.25E-05,7.97E-05 +750,2.02E-03,2.14E-01,2.95E-03,9.07E-04,3.86E-05,4.72E-07,6.42E-04,3.25E-06,3.72E-04,7.38E-07,0.00E+00,6.83E-07,1.08E-03,2.95E-05,6.98E-04,1.82E-04,2.02E-06,2.40E-05,3.26E-06,4.69E-06,1.66E-05,5.93E-06,1.41E-04,1.43E-03,6.71E-05,9.16E-05,2.33E-06,2.66E-06,1.18E-05,1.20E-05,2.17E-06,4.74E-05,1.64E-05,1.12E-04,4.83E-05,1.49E-05,2.95E-05,0.00E+00,2.75E-05,4.59E-07,8.12E-06,1.14E-04,6.00E-05,2.19E-05,6.65E-06,7.71E-06,0.00E+00,0.00E+00,5.71E-04,4.91E-04,9.27E-05,1.31E-05,7.76E-05 +775,1.79E-03,2.12E-01,3.65E-03,1.15E-03,8.60E-05,1.13E-06,8.06E-04,3.46E-06,4.70E-04,7.38E-07,0.00E+00,1.02E-06,1.11E-03,4.13E-05,7.21E-04,2.30E-04,2.83E-06,4.00E-05,4.34E-06,4.02E-06,2.01E-05,6.43E-06,1.61E-04,1.36E-03,6.91E-05,1.15E-04,3.25E-06,4.14E-06,1.81E-05,1.38E-05,2.41E-06,4.84E-05,1.87E-05,9.84E-05,4.73E-05,2.03E-05,3.05E-05,0.00E+00,2.50E-05,7.96E-07,7.48E-06,1.14E-04,5.93E-05,2.13E-05,6.65E-06,9.89E-06,0.00E+00,0.00E+00,4.44E-04,3.98E-04,6.90E-05,1.02E-05,6.63E-05 +800,1.21E-03,2.07E-01,5.78E-03,1.39E-03,2.13E-04,2.71E-06,1.27E-03,8.65E-06,6.41E-04,7.38E-07,0.00E+00,1.48E-06,1.20E-03,5.31E-05,8.58E-04,2.83E-04,3.88E-06,6.63E-05,6.51E-06,3.12E-06,2.77E-05,6.92E-06,1.94E-04,1.25E-03,7.32E-05,1.30E-04,4.43E-06,5.91E-06,2.41E-05,2.27E-05,2.96E-06,6.32E-05,2.25E-05,8.20E-05,4.52E-05,2.37E-05,3.69E-05,0.00E+00,2.10E-05,1.28E-06,6.48E-06,9.57E-05,5.01E-05,1.82E-05,3.18E-06,7.12E-06,0.00E+00,0.00E+00,2.57E-04,2.37E-04,4.84E-05,4.27E-06,3.88E-05 +825,7.60E-04,2.02E-01,9.22E-03,1.66E-03,4.11E-04,5.65E-06,1.87E-03,1.52E-05,7.49E-04,1.34E-06,1.08E-06,1.94E-06,1.08E-03,7.09E-05,7.78E-04,2.91E-04,4.97E-06,8.84E-05,6.88E-06,1.93E-06,3.07E-05,7.42E-06,2.02E-04,9.15E-04,6.10E-05,1.21E-04,4.14E-06,5.61E-06,2.65E-05,2.71E-05,2.83E-06,5.77E-05,1.94E-05,5.36E-05,3.02E-05,2.35E-05,3.39E-05,0.00E+00,1.34E-05,1.66E-06,5.83E-06,6.37E-05,3.34E-05,1.21E-05,1.45E-06,2.57E-06,0.00E+00,0.00E+00,1.42E-04,1.37E-04,2.72E-05,3.06E-06,2.07E-05 +850,5.44E-04,2.01E-01,1.39E-02,2.27E-03,5.99E-04,1.24E-05,2.38E-03,2.28E-05,6.97E-04,1.38E-06,1.41E-06,2.45E-06,9.62E-04,8.86E-05,5.26E-04,2.42E-04,4.04E-06,8.84E-05,5.43E-06,7.44E-07,2.11E-05,5.44E-06,1.60E-04,4.90E-04,3.15E-05,9.75E-05,2.95E-06,4.73E-06,1.90E-05,2.27E-05,2.35E-06,2.51E-05,1.26E-05,2.51E-05,1.31E-05,2.19E-05,2.60E-05,0.00E+00,7.24E-06,1.78E-06,2.84E-06,3.51E-05,1.84E-05,5.85E-06,2.89E-07,0.00E+00,0.00E+00,0.00E+00,9.62E-05,9.56E-05,1.58E-05,1.25E-06,1.59E-05 +875,2.82E-04,1.92E-01,1.60E-02,3.09E-03,7.16E-04,1.71E-05,2.87E-03,2.77E-05,6.10E-04,1.11E-06,1.34E-06,2.85E-06,6.01E-04,7.79E-05,3.89E-04,1.90E-04,2.62E-06,8.08E-05,3.98E-06,0.00E+00,1.56E-05,4.40E-06,1.29E-04,2.94E-04,1.43E-05,7.39E-05,2.30E-06,3.84E-06,1.36E-05,1.95E-05,1.75E-06,1.51E-05,9.52E-06,1.45E-05,6.03E-06,1.89E-05,1.65E-05,0.00E+00,3.98E-06,1.23E-06,3.49E-07,1.92E-05,9.82E-06,2.92E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,2.53E-05,2.17E-05,4.61E-06,4.93E-07,2.41E-06 +900,1.18E-04,1.88E-01,1.84E-02,5.31E-03,5.95E-04,1.87E-05,2.29E-03,2.28E-05,3.64E-04,9.22E-07,1.01E-06,2.28E-06,3.85E-04,5.55E-05,2.27E-04,1.09E-04,1.49E-06,5.18E-05,2.17E-06,0.00E+00,6.54E-06,2.13E-06,7.47E-05,1.34E-04,3.40E-08,4.14E-05,1.54E-06,1.95E-06,6.70E-06,1.26E-05,1.09E-06,4.29E-06,4.19E-06,7.24E-06,1.51E-06,1.16E-05,8.49E-06,0.00E+00,2.90E-06,5.55E-07,0.00E+00,8.36E-06,3.34E-06,1.02E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,9.03E-06,2.17E-06,9.91E-07,0.00E+00,0.00E+00 +925,2.30E-05,1.84E-01,1.94E-02,1.01E-02,3.76E-04,1.57E-05,1.19E-03,1.38E-05,1.53E-04,0.00E+00,6.72E-07,1.82E-06,1.44E-04,2.36E-05,1.14E-04,4.04E-05,6.06E-07,2.65E-05,7.24E-07,0.00E+00,2.06E-06,1.19E-06,3.60E-05,5.81E-05,0.00E+00,1.33E-05,8.57E-07,9.75E-07,3.53E-06,4.72E-06,6.03E-07,1.50E-06,9.90E-07,2.90E-06,5.03E-07,3.39E-06,3.99E-06,0.00E+00,7.24E-07,0.00E+00,0.00E+00,2.03E-06,7.31E-07,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,6.26E-07,0.00E+00,2.07E-07,0.00E+00,0.00E+00 +950,2.12E-06,1.78E-01,1.61E-02,1.68E-02,2.14E-04,9.17E-06,5.96E-04,6.92E-06,6.40E-05,0.00E+00,0.00E+00,1.42E-06,4.81E-05,7.09E-06,5.95E-05,1.29E-05,0.00E+00,1.34E-05,2.90E-07,0.00E+00,8.56E-07,4.94E-07,1.53E-05,2.18E-05,0.00E+00,3.84E-06,0.00E+00,0.00E+00,1.48E-06,1.95E-06,0.00E+00,4.99E-07,0.00E+00,1.45E-06,0.00E+00,1.00E-06,1.90E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00 +975,0.00E+00,1.75E-01,1.28E-02,2.34E-02,1.12E-04,4.64E-06,2.46E-04,3.46E-06,2.12E-05,0.00E+00,0.00E+00,0.00E+00,0.00E+00,2.01E-06,2.52E-05,4.04E-06,0.00E+00,5.05E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,6.03E-06,6.54E-06,0.00E+00,9.75E-07,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,1.25E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00 +1000,0.00E+00,1.70E-01,8.39E-03,2.62E-02,5.54E-05,1.93E-06,8.57E-05,8.31E-07,3.36E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,8.01E-06,6.06E-07,0.00E+00,9.68E-07,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00 +1050,0.00E+00,1.66E-01,3.18E-03,3.32E-02,1.17E-05,1.11E-07,3.76E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,2.29E-06,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00 +1100,0.00E+00,1.64E-01,0.00E+00,3.55E-02,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00,0.00E+00 diff --git a/pyked/tests/test_chemked.py b/pyked/tests/test_chemked.py index 8314564..651916a 100644 --- a/pyked/tests/test_chemked.py +++ b/pyked/tests/test_chemked.py @@ -15,7 +15,7 @@ # Local imports from ..validation import schema, OurValidator, yaml, Q_ -from ..chemked import ChemKED, DataPoint, Composition +from ..chemked import ChemKED, IgnitionDataPoint, Composition from ..converters import get_datapoints, get_common_properties from .._version import __version__ @@ -83,7 +83,7 @@ def test_unallowed_input(self, capfd): out, err = capfd.readouterr() assert out == ("experiment-type has an illegal value. Allowed values are ['ignition " - "delay'] and are case sensitive.\n") + "delay', 'species profile'] and are case sensitive.\n") def test_missing_input(self, capfd): file_path = os.path.join('testfile_required.yaml') @@ -547,18 +547,18 @@ def load_properties(self, test_file): def test_create_datapoint(self): properties = self.load_properties('testfile_required.yaml') - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) def test_cantera_unknown_composition_type(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) d.composition_type = 'unknown type' with pytest.raises(ValueError): d.get_cantera_composition_string() def test_cantera_composition_mole_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -569,14 +569,14 @@ def test_cantera_composition_mole_fraction(self): def test_cantera_composition_mole_fraction_bad(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) assert d.composition_type == 'mass fraction' with pytest.raises(ValueError): d.get_cantera_mole_fraction() def test_cantera_composition_mass_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -587,14 +587,14 @@ def test_cantera_composition_mass_fraction(self): def test_cantera_composition_mass_fraction_bad(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert d.composition_type == 'mole fraction' with pytest.raises(ValueError): d.get_cantera_mass_fraction() def test_cantera_composition_mole_percent(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[2]) + d = IgnitionDataPoint(properties[2]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -605,7 +605,7 @@ def test_cantera_composition_mole_percent(self): def test_cantera_change_species_by_name_mole_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -616,7 +616,7 @@ def test_cantera_change_species_by_name_mole_fraction(self): def test_cantera_change_species_by_inchi_mole_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -627,7 +627,7 @@ def test_cantera_change_species_by_inchi_mole_fraction(self): def test_cantera_change_species_by_name_mole_percent(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[2]) + d = IgnitionDataPoint(properties[2]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -638,7 +638,7 @@ def test_cantera_change_species_by_name_mole_percent(self): def test_cantera_change_species_by_inchi_mole_percent(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[2]) + d = IgnitionDataPoint(properties[2]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -649,7 +649,7 @@ def test_cantera_change_species_by_inchi_mole_percent(self): def test_cantera_change_species_by_name_mass_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -660,7 +660,7 @@ def test_cantera_change_species_by_name_mass_fraction(self): def test_cantera_change_species_by_inchi_mass_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) # The order of the keys should not change between calls provided the contents of the # dictionary don't change. Therefore, spec_order should be the same order as the # Cantera mole fraction string constructed in a loop in the code @@ -671,35 +671,35 @@ def test_cantera_change_species_by_inchi_mass_fraction(self): def test_cantera_change_species_missing_mole_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) species_conversion = {'this-does-not-exist': 'h2', 'O2': 'o2'} with pytest.raises(ValueError): d.get_cantera_mole_fraction(species_conversion) def test_cantera_change_species_missing_mass_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) species_conversion = {'this-does-not-exist': 'h2', 'O2': 'o2'} with pytest.raises(ValueError): d.get_cantera_mass_fraction(species_conversion) def test_cantera_change_species_duplicate_mole_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) species_conversion = {'H2': 'h2', '1S/H2/h1H': 'h2'} with pytest.raises(ValueError): d.get_cantera_mole_fraction(species_conversion) def test_cantera_change_species_duplicate_mass_fraction(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) species_conversion = {'H2': 'h2', '1S/H2/h1H': 'h2'} with pytest.raises(ValueError): d.get_cantera_mass_fraction(species_conversion) def test_composition(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[2]) + d = IgnitionDataPoint(properties[2]) assert len(d.composition) == 3 assert np.isclose(d.composition['H2'].amount, Q_(0.444)) assert d.composition['H2'].species_name == 'H2' @@ -710,23 +710,23 @@ def test_composition(self): def test_ignition_delay(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.ignition_delay, Q_(471.54, 'us')) def test_first_stage_ignition_delay(self): properties = self.load_properties('testfile_rcm2.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.first_stage_ignition_delay.value, Q_(0.5, 'ms')) assert np.isclose(d.first_stage_ignition_delay.error, Q_(0.005, 'ms')) def test_temperature(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.temperature, Q_(1164.48, 'K')) def test_rcm_data(self): properties = self.load_properties('testfile_rcm2.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.rcm_data.compression_time, Q_(38.0, 'ms')) assert np.isclose(d.rcm_data.compressed_temperature.value, Q_(765, 'K')) assert np.isclose(d.rcm_data.compressed_temperature.error, Q_(7.65, 'K')) @@ -737,32 +737,32 @@ def test_rcm_data(self): def test_pressure(self): properties = self.load_properties('testfile_required.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.pressure, Q_(220.0, 'kPa')) def test_pressure_rise(self): properties = self.load_properties('testfile_st2.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.pressure_rise, Q_(0.1, '1/ms')) @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') def test_absolute_sym_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.temperature.value, Q_(1164.48, 'K')) assert np.isclose(d.temperature.error, Q_(10, 'K')) @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') def test_absolute_sym_comp_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.composition['O2'].amount.value, Q_(0.556)) assert np.isclose(d.composition['O2'].amount.error, Q_(0.002)) @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') def test_relative_sym_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) assert np.isclose(d.ignition_delay.value, Q_(471.54, 'us')) assert np.isclose(d.ignition_delay.error, Q_(47.154, 'us')) assert np.isclose(d.ignition_delay.rel, 0.1) @@ -770,7 +770,7 @@ def test_relative_sym_uncertainty(self): @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') def test_relative_sym_comp_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) assert np.isclose(d.composition['H2'].amount.value, Q_(0.444)) assert np.isclose(d.composition['H2'].amount.error, Q_(0.00444)) assert np.isclose(d.composition['H2'].amount.rel, 0.01) @@ -778,7 +778,7 @@ def test_relative_sym_comp_uncertainty(self): def test_absolute_asym_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') with pytest.warns(UserWarning) as record: - d = DataPoint(properties[2]) + d = IgnitionDataPoint(properties[2]) m = str(record.pop(UserWarning).message) assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 'and upper-uncertainty has been used as the symmetric uncertainty.') @@ -790,7 +790,7 @@ def test_absolute_asym_uncertainty(self): def test_relative_asym_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') with pytest.warns(UserWarning) as record: - d = DataPoint(properties[3]) + d = IgnitionDataPoint(properties[3]) m = str(record.pop(UserWarning).message) assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 'and upper-uncertainty has been used as the symmetric uncertainty.') @@ -804,7 +804,7 @@ def test_relative_asym_uncertainty(self): def test_absolute_asym_comp_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') with pytest.warns(UserWarning) as record: - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) m = str(record.pop(UserWarning).message) assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 'and upper-uncertainty has been used as the symmetric uncertainty.') @@ -812,7 +812,7 @@ def test_absolute_asym_comp_uncertainty(self): assert np.isclose(d.composition['Ar'].amount.error, Q_(1.0)) with pytest.warns(UserWarning) as record: - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) m = str(record.pop(UserWarning).message) assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 'and upper-uncertainty has been used as the symmetric uncertainty.') @@ -822,7 +822,7 @@ def test_absolute_asym_comp_uncertainty(self): def test_relative_asym_comp_uncertainty(self): properties = self.load_properties('testfile_uncertainty.yaml') with pytest.warns(UserWarning) as record: - d = DataPoint(properties[1]) + d = IgnitionDataPoint(properties[1]) m = str(record.pop(UserWarning).message) assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 'and upper-uncertainty has been used as the symmetric uncertainty.') @@ -840,23 +840,23 @@ def test_missing_uncertainty_parts(self): for prop in ['uncertainty', 'uncertainty-type']: save = properties[0]['temperature'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) properties[0]['temperature'][1][prop] = save save = properties[1]['ignition-delay'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[1]) + IgnitionDataPoint(properties[1]) properties[1]['ignition-delay'][1][prop] = save for prop in ['upper-uncertainty', 'lower-uncertainty']: save = properties[2]['temperature'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[2]) + IgnitionDataPoint(properties[2]) properties[0]['temperature'][1][prop] = save save = properties[3]['ignition-delay'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[3]) + IgnitionDataPoint(properties[3]) properties[1]['ignition-delay'][1][prop] = save @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') @@ -865,23 +865,23 @@ def test_missing_comp_uncertainty_parts(self): for prop in ['uncertainty', 'uncertainty-type']: save = properties[0]['composition']['species'][0]['amount'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) properties[0]['composition']['species'][0]['amount'][1][prop] = save save = properties[0]['composition']['species'][1]['amount'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) properties[0]['composition']['species'][1]['amount'][1][prop] = save for prop in ['upper-uncertainty', 'lower-uncertainty']: save = properties[0]['composition']['species'][2]['amount'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) properties[0]['composition']['species'][2]['amount'][1][prop] = save save = properties[1]['composition']['species'][2]['amount'][1].pop(prop) with pytest.raises(ValueError): - DataPoint(properties[1]) + IgnitionDataPoint(properties[1]) properties[1]['composition']['species'][2]['amount'][1][prop] = save def test_volume_history(self): @@ -891,7 +891,7 @@ def test_volume_history(self): """ properties = self.load_properties('testfile_rcm_old.yaml') with pytest.warns(DeprecationWarning) as record: - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) m = str(record.pop(DeprecationWarning).message) assert m == ('The volume-history field should be replaced by time-histories. ' 'volume-history will be removed after PyKED 0.4') @@ -942,7 +942,7 @@ def test_time_and_volume_histories_error(self): properties = self.load_properties('testfile_rcm.yaml') properties[0]['volume-history'] = {} with pytest.raises(TypeError) as record: - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) assert 'time-histories and volume-history are mutually exclusive' in str(record.value) @@ -954,7 +954,7 @@ def test_time_histories_array(self, history_type): """Check that all of the history types are set properly""" properties = self.load_properties('testfile_rcm.yaml') properties[0]['time-histories'][0]['type'] = history_type - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type)).time, Q_(np.arange(0, 9.7e-2, 1.e-3), 's') @@ -1006,7 +1006,7 @@ def test_time_histories_file(self, history_type): file_path = os.path.join('rcm_history.csv') filename = pkg_resources.resource_filename(__name__, file_path) properties[0]['time-histories'][0]['values'] = {'filename': filename} - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type)).time, Q_(np.arange(0, 9.7e-2, 1.e-3), 's') @@ -1061,7 +1061,7 @@ def test_multiple_time_histories(self, history_type): properties[0]['time-histories'][0]['type'] = history_type[0] properties[0]['time-histories'].append(deepcopy(properties[0]['time-histories'][0])) properties[0]['time-histories'][1]['type'] = history_type[1] - d = DataPoint(properties[0]) + d = IgnitionDataPoint(properties[0]) np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type[0])).time, Q_(np.arange(0, 9.7e-2, 1.e-3), 's')) @@ -1116,35 +1116,35 @@ def test_duplicate_time_histories(self, history_type): properties[0]['time-histories'].append(deepcopy(properties[0]['time-histories'][0])) properties[0]['time-histories'][1]['type'] = history_type[1] with pytest.raises(ValueError) as record: - DataPoint(properties[0]) + IgnitionDataPoint(properties[0]) assert ('Each history type may only be specified once. {} was ' 'specified multiple times'.format(history_type[0])) in str(record.value) def test_supported_ignition_types(self): # pressure d/dt max properties = self.load_properties('testfile_st.yaml') - datapoints = [DataPoint(d) for d in properties] + datapoints = [IgnitionDataPoint(d) for d in properties] for d in datapoints: assert d.ignition_type['target'] == 'pressure' assert d.ignition_type['type'] == 'd/dt max' # OH, max properties = self.load_properties('testfile_st2.yaml') - datapoints = [DataPoint(d) for d in properties] + datapoints = [IgnitionDataPoint(d) for d in properties] for d in datapoints: assert d.ignition_type['target'] == 'OH' assert d.ignition_type['type'] == 'max' # OH*, 1/2 max properties = self.load_properties('testfile_st_p5.yaml') - datapoints = [DataPoint(d) for d in properties] + datapoints = [IgnitionDataPoint(d) for d in properties] for d in datapoints: assert d.ignition_type['target'] == 'OH*' assert d.ignition_type['type'] == '1/2 max' # CH, min properties = self.load_properties('testfile_required.yaml') - datapoints = [DataPoint(d) for d in properties] + datapoints = [IgnitionDataPoint(d) for d in properties] assert datapoints[0].ignition_type['target'] == 'CH' assert datapoints[0].ignition_type['type'] == 'min' @@ -1154,7 +1154,7 @@ def test_supported_ignition_types(self): def test_changing_ignition_type(self): properties = self.load_properties('testfile_st.yaml') - datapoints = [DataPoint(d) for d in properties] + datapoints = [IgnitionDataPoint(d) for d in properties] datapoints[0].ignition_type['target'] = 'temperature' assert datapoints[0].ignition_type['target'] == 'temperature' for d in datapoints[1:]: diff --git a/pyked/tests/test_validation.py b/pyked/tests/test_validation.py index 50c46f0..0810964 100644 --- a/pyked/tests/test_validation.py +++ b/pyked/tests/test_validation.py @@ -359,7 +359,7 @@ def properties(self, request): filename = pkg_resources.resource_filename(__name__, file_path) with open(filename, 'r') as f: - return yaml.load(f) + return yaml.safe_load(f) @pytest.mark.parametrize("properties", [ 'testfile_st.yaml', 'testfile_st2.yaml', 'testfile_rcm.yaml', 'testfile_required.yaml', diff --git a/pyked/tests/testfile_jsr1.yaml b/pyked/tests/testfile_jsr1.yaml new file mode 100644 index 0000000..9b0ca77 --- /dev/null +++ b/pyked/tests/testfile_jsr1.yaml @@ -0,0 +1,179 @@ +--- +file-authors: + - name: Anthony Stohr + ORCID: 0000-0002-2388-1723 + - name: Benjamin Hoare + ORCID: 0000-0001-7205-0777 +file-version: 1.0 +chemked-version: 0.4.1 +reference: + doi: 10.1016/j.combustflame.2016.06.028 + authors: + - name: Kuiwen Zhang + - name: Colin Banyon + - name: John Bugler + - name: Henry J. Curran + ORCID: 0000-0002-5124-8562 + - name: Anne Rodriguez + - name: Olivier Herbinet + - name: Frédérique Battin-Leclerc + ORCID: 0000-0001-8265-7492 + - name: Christine BChir + - name: Karl Alexander Heufer + ORCID: 0000-0003-1657-5231 + journal: Combustion and Flame + year: 2016 + volume: 172 + pages: 116-135 + detail: An updated experimental and kinetic modeling study of n-heptane oxidation + +experiment-type: species profile +apparatus: + kind: jet stirred reactor + institution: University of Lorraine, Nancy, France + facility: LRGP JSR + +datapoints: + - csvfile: new_zhang_phi0.25.csv + temperature: + - column-name: Temperature # values are provided in the CSV file + - units: K + - uncertainty-type: absolute + uncertainty: 3 K + pressure: + - 1.067 bar + - uncertainty-type: absolute + uncertainty: 0.01 bar # made up for testing + reactor-volume: + - 0.095 litres + residence-time: + - 2 s + inlet-composition: + kind: mole fraction + species: + - species-name: n-heptane + InChI: 1S/C7H16/c1-3-5-7-6-4-2/h3-7H2,1-2H3 + amount: + - 0.005 + - uncertainty-type: relative + uncertainty: 0.05 + - species-name: He + InChI: 1S/He + amount: + - 0.775 + - uncertainty-type: relative + uncertainty: 0.05 + - species-name: oxygen + InChI: 1S/O2/c1-2 + amount: + - 0.22 + - uncertainty-type: relative + uncertainty: 0.05 + outlet-composition: &out + kind: mole fraction + species: + - species-name: carbon monoxide # species names must correspond with column headers in the CSV file + InChI: 1S/CH2O/c1-2/h1H2 + - species-name: carbon dioxide + InChI: 1S/CO2/c2-1-3 + - species-name: methane + InChI: 1S/CH4/h1H4 + - species-name: acetylene + InChI: 1S/C2H2/c1-2/h1-2H + - species-name: ethylene + InChI: 1S/C2H4/c1-2/h1-2H2 + - species-name: ethane + InChI: 1S/C2H6/c1-2/h1-2H3 + - species-name: propene + InChI: 1S/C3H6/c1-3-2/h3H,1H2,2H3 + - species-name: propane + InChI: 1S/C3H8/c1-3-2/h3H2,1-2H3 + - species-name: propadiene + InChI: 1S/C3H4/c1-3-2/h1-2H2 + - species-name: propyne + InChI: 1S/C3H4/c1-3-2/h1H,2H3 + - species-name: formaldehyde + InChI: 1S/CH2O/c1-2/h1H2 + - species-name: ethylene oxide + InChI: 1S/C2H4O/c1-2-3-1/h1-2H2 + - species-name: acetaldehyde + InChI: 1S/C2H4O/c1-2-3/h2H,1H3 + - species-name: 1-butene + InChI: 1S/C4H8/c1-3-4-2/h3H,1,4H2,2H3 + - species-name: 2-butene + InChI: 1S/C4H8/c1-3-4-2/h3-4H,1-2H3 + - species-name: 1,3-butadiene + InChI: 1S/C4H6/c1-3-4-2/h3-4H,1-2H2 + - species-name: n-butane + InChI: 1S/C4H10/c1-3-4-2/h3-4H2,1-2H3 + - species-name: ethanol + InChI: 1S/C2H6O/c1-2-3/h3H,2H2,1H3 + - species-name: propylene oxide + InChI: 1S/C3H6O/c1-3-2-4-3/h3H,2H2,1H3 + - species-name: furan + InChI: 1S/C4H4O/c1-2-4-5-3-1/h1-4H + - species-name: acrolein + InChI: 1S/C3H4O/c1-2-3-4/h2-3H,1H2 + - species-name: propanal + InChI: 1S/C3H8O/c1-2-3-4/h4H,2-3H2,1H3 + - species-name: acetone + InChI: 1S/C3H6O/c1-3(2)4/h1-2H3 + - species-name: 1-pentene + InChI: 1S/C5H10/c1-3-5-4-2/h3H,1,4-5H2,2H3 + - species-name: 2-pentene + InChI: 1S/C5H10/c1-3-5-4-2/h3,5H,4H2,1-2H3 + - species-name: cyclopentene + InChI: 1S/C5H8/c1-2-4-5-3-1/h1-2H,3-5H2 + - species-name: 1,3-pentadiene + InChI: 1S/C5H8/c1-3-5-4-2/h3-5H,1H2,2H3 + - species-name: 2-propen-1-ol + InChI: 1S/C3H6O/c1-2-3-4/h2,4H,1,3H2 + - species-name: propan-1-ol + InChI: 1S/C3H8O/c1-2-3-4/h4H,2-3H2,1H3 + - species-name: butenone + InChI: 1S/C4H6O/c1-3-4(2)5/h3H,1H2,2H3 + - species-name: methyl-furan + InChI: 1S/C5H6O/c1-5-3-2-4-6-5/h2-4H,1H3 + - species-name: butanal + InChI: 1S/C5H6O/c1-5-3-2-4-6-5/h2-4H,1H3 + - species-name: 2-butanone + InChI: 1S/C4H8O/c1-3-4(2)5/h3H2,1-2H3 + - species-name: 1-hexene + InChI: 1S/C6H12/c1-3-5-6-4-2/h3H,1,4-6H2,2H3 + - species-name: 2-butenal + InChI: 1S/C4H6O/c1-2-3-4-5/h2-4H,1H3 + - species-name: acetic acid + InChI: 1S/C2H4O2/c1-2(3)4/h1H3,(H,3,4) + - species-name: pentanal + pentanone + InChI: 1S/C5H10O/c1-2-3-4-5-6/h5H,2-4H2,1H3 + - species-name: benzene + InChI: 1S/C6H6/c1-2-4-6-5-3-1/h1-6H + - species-name: dihydrofuran + InChI: 1S/C4H6O/c1-2-4-5-3-1/h1,3H,2,4H2 + - species-name: 3-heptene + InChI: 1S/C7H14/c1-3-5-7-6-4-2/h5,7H,3-4,6H2,1-2H3 + - species-name: 2-heptene + InChI: 1S/C7H14/c1-3-5-7-6-4-2/h3,5H,4,6-7H2,1-2H3 + - species-name: 1-heptene + InChI: 1S/C7H14/c1-3-5-7-6-4-2/h3H,1,4-7H2,2H3 + - species-name: 2-hexanone + InChI: 1S/C6H12O/c1-3-4-5-6(2)7/h3-5H2,1-2H3 + - species-name: propanoic acid + InChI: 1S/C3H6O2/c1-2-3(4)5/h2H2,1H3,(H,4,5) + - species-name: 2-ethyl-5-methyl-furan + InChI: 1S/C7H10O/c1-3-7-5-4-6(2)8-7/h4-5H,3H2,1-2H3 + - species-name: 2-methyl-dihydrofuranone + InChI: 1S/C5H8O2/c1-4-5(6)2-3-7-4/h4H,2-3H2,1H3 + - species-name: sum of cyclic ether with 5 atoms + # could try to get fancy and do SMARTS strings instead of InChI + # or we could just specify "unknown" somehow. + # We don't want to discard the data, even if we can't automatically + # compare to simulations. + - species-name: sum of cyclic ether with 4 atoms + - species-name: sum of cyclic ether with 3 atoms + - species-name: 2methanol-5methyl-tetrahydrofuranone + InChI: 1S/C6H10O3/c1-4-6(8)2-5(3-7)9-4/h4-5,7H,2-3H2,1H3 + - species-name: heptanone + InChI: 1S/C7H14O/c1-3-4-5-6-7(2)8/h3-6H2,1-2H3 + +