From e6bb395ada6f40e4d8ef5567dcfc184f466bc30a Mon Sep 17 00:00:00 2001 From: Kyle Niemeyer Date: Sun, 20 May 2018 15:37:01 -0700 Subject: [PATCH 01/15] updated year in CITATION --- CITATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CITATION.md b/CITATION.md index 6cc32ef..26f7a6b 100644 --- a/CITATION.md +++ b/CITATION.md @@ -11,7 +11,7 @@ A BibTeX entry for LaTeX users is ```TeX @misc{PyKED, author = {Kyle E Niemeyer and Bryan W Weber}, - year = 2017, + year = 2018, title = {PyKED v0.4.1}, doi = {10.5281/zenodo.597935}, url = {https://github.com/pr-omethe-us/PyKED}, From aed0699ca3f7dfa0dc12a2d46075eaf4c643b81e Mon Sep 17 00:00:00 2001 From: Jeff Santner Date: Wed, 13 Jun 2018 17:08:14 -0700 Subject: [PATCH 02/15] Add test for reading time-history from file --- pyked/tests/test_validation.py | 2 +- pyked/tests/testfile_rcm3.yaml | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 pyked/tests/testfile_rcm3.yaml diff --git a/pyked/tests/test_validation.py b/pyked/tests/test_validation.py index 50c46f0..e9d2c1b 100644 --- a/pyked/tests/test_validation.py +++ b/pyked/tests/test_validation.py @@ -363,7 +363,7 @@ def properties(self, request): @pytest.mark.parametrize("properties", [ 'testfile_st.yaml', 'testfile_st2.yaml', 'testfile_rcm.yaml', 'testfile_required.yaml', - 'testfile_uncertainty.yaml', 'testfile_rcm2.yaml', + 'testfile_uncertainty.yaml', 'testfile_rcm2.yaml', 'testfile_rcm3.yaml' ], indirect=['properties']) def test_valid_yaml(self, properties): """Ensure ChemKED YAML is validated diff --git a/pyked/tests/testfile_rcm3.yaml b/pyked/tests/testfile_rcm3.yaml new file mode 100644 index 0000000..b3ed39b --- /dev/null +++ b/pyked/tests/testfile_rcm3.yaml @@ -0,0 +1,66 @@ +--- +file-authors: + - name: Kyle E Niemeyer + ORCID: 0000-0003-4425-7097 +file-version: 0 +chemked-version: 0.0.1 +reference: + doi: 10.1002/kin.20180 + authors: + - name: Gaurav Mittal + - name: Chih-Jen Sung + ORCID: 0000-0003-2046-8076 + - name: Richard A Yetter + journal: International Journal of Chemical Kinetics + year: 2006 + volume: 38 + pages: 516-529 + detail: Fig. 6, open circle +experiment-type: ignition delay +apparatus: + kind: rapid compression machine + institution: Case Western Reserve University + facility: CWRU RCM +datapoints: + - temperature: + - 297.4 kelvin + ignition-delay: + - 1.0 ms + pressure: + - 958.0 torr + composition: + kind: mole fraction + species: + - species-name: H2 + InChI: 1S/H2/h1H + amount: + - 0.12500 + - species-name: O2 + InChI: 1S/O2/c1-2 + amount: + - 0.06250 + - species-name: N2 + InChI: 1S/N2/c1-2 + amount: + - 0.18125 + - species-name: Ar + InChI: 1S/Ar + amount: + - 0.63125 + ignition-type: + target: pressure + type: d/dt max + rcm-data: + compression-time: + - 38.0 ms + time-histories: + - type: volume + time: + units: s + column: 0 + quantity: + units: cm3 + column: 1 + values: + filename: rcm_history.csv +... From c2e36f729b1728deb7c55d10367d061778dbbf85 Mon Sep 17 00:00:00 2001 From: Jeff Santner Date: Wed, 13 Jun 2018 17:13:55 -0700 Subject: [PATCH 03/15] force travis test --- pyked/tests/testfile_rcm3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyked/tests/testfile_rcm3.yaml b/pyked/tests/testfile_rcm3.yaml index b3ed39b..e5dfefb 100644 --- a/pyked/tests/testfile_rcm3.yaml +++ b/pyked/tests/testfile_rcm3.yaml @@ -62,5 +62,5 @@ datapoints: units: cm3 column: 1 values: - filename: rcm_history.csv + filename: rcm_history.csv ... From b8469964b1756ffc5a7b2b22b79caf2516d28be1 Mon Sep 17 00:00:00 2001 From: Jeff Santner Date: Thu, 14 Jun 2018 09:55:58 -0700 Subject: [PATCH 04/15] Don't test number of columns when reading time-history from file --- pyked/validation.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pyked/validation.py b/pyked/validation.py index 4814201..dcd4d92 100644 --- a/pyked/validation.py +++ b/pyked/validation.py @@ -252,14 +252,18 @@ def _validate_isvalid_history(self, isvalid_history, field, value): 'with ' + property_units['time']) # Check that the values have the right number of columns - n_cols = len(value['values'][0]) - max_cols = max(value['time']['column'], - value['quantity']['column'], - value.get('uncertainty', {}).get('column', 0)) + 1 - if n_cols > max_cols: - self._error(field, 'too many columns in the values') - elif n_cols < max_cols: - self._error(field, 'not enough columns in the values') + # If reading from a file, the file will not be validated. + # A file can have an arbitrary number of columns, and the columns + # to be used are specified. + if 'filename' not in value.keys(): + n_cols = len(value['values'][0]) + max_cols = max(value['time']['column'], + value['quantity']['column'], + value.get('uncertainty', {}).get('column', 0)) + 1 + if n_cols > max_cols: + self._error(field, 'too many columns in the values') + elif n_cols < max_cols: + self._error(field, 'not enough columns in the values') def _validate_isvalid_quantity(self, isvalid_quantity, field, value): """Checks for valid given value and appropriate units. From abe35c49c489450d088d4c0c9ee37dd1235daa21 Mon Sep 17 00:00:00 2001 From: Jeff Santner Date: Thu, 14 Jun 2018 10:17:15 -0700 Subject: [PATCH 05/15] Minor fix on dictionary structure --- pyked/validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyked/validation.py b/pyked/validation.py index dcd4d92..1e82877 100644 --- a/pyked/validation.py +++ b/pyked/validation.py @@ -255,7 +255,7 @@ def _validate_isvalid_history(self, isvalid_history, field, value): # If reading from a file, the file will not be validated. # A file can have an arbitrary number of columns, and the columns # to be used are specified. - if 'filename' not in value.keys(): + if 'filename' not in value['values'].keys(): n_cols = len(value['values'][0]) max_cols = max(value['time']['column'], value['quantity']['column'], From c35b6811793802dc58895e7408030e20ba6565e2 Mon Sep 17 00:00:00 2001 From: Jeff Santner Date: Thu, 14 Jun 2018 10:31:48 -0700 Subject: [PATCH 06/15] Make it work without filename --- pyked/validation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyked/validation.py b/pyked/validation.py index 1e82877..3c62c4b 100644 --- a/pyked/validation.py +++ b/pyked/validation.py @@ -255,7 +255,7 @@ def _validate_isvalid_history(self, isvalid_history, field, value): # If reading from a file, the file will not be validated. # A file can have an arbitrary number of columns, and the columns # to be used are specified. - if 'filename' not in value['values'].keys(): + if type(value['values']) is list: n_cols = len(value['values'][0]) max_cols = max(value['time']['column'], value['quantity']['column'], @@ -264,6 +264,8 @@ def _validate_isvalid_history(self, isvalid_history, field, value): self._error(field, 'too many columns in the values') elif n_cols < max_cols: self._error(field, 'not enough columns in the values') + elif 'filename' not in value['values'].keys(): + self._error(field, 'must include filename or list of values') def _validate_isvalid_quantity(self, isvalid_quantity, field, value): """Checks for valid given value and appropriate units. From 5d851fd28852a18389d15d6cd4f425311c728756 Mon Sep 17 00:00:00 2001 From: jsantner Date: Thu, 14 Jun 2018 14:41:52 -0700 Subject: [PATCH 07/15] When loading a csv file, tell DataPoint which directory to look in --- pyked/chemked.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pyked/chemked.py b/pyked/chemked.py index a8573e9..0aae09a 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -2,7 +2,7 @@ Main ChemKED module """ # Standard libraries -from os.path import exists +from os.path import exists, isabs, dirname, join from collections import namedtuple from warnings import warn from copy import deepcopy @@ -121,7 +121,7 @@ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): self.datapoints = [] for point in self._properties['datapoints']: - self.datapoints.append(DataPoint(point)) + self.datapoints.append(DataPoint(point, dirname(yaml_file))) self.reference = Reference( volume=self._properties['reference'].get('volume'), @@ -589,6 +589,7 @@ class DataPoint(object): Arguments: properties (`dict`): Dictionary adhering to the ChemKED format for ``datapoints`` + directory (`str`, optional): Directory to look for auxiliary files Attributes: composition (`list`): List of dictionaries representing the species and their quantities @@ -631,7 +632,7 @@ class DataPoint(object): 'compression-ratio' ] - def __init__(self, properties): + def __init__(self, properties, directory=None): for prop in self.value_unit_props: if prop in properties: quant = self.process_quantity(properties[prop]) @@ -685,7 +686,10 @@ def __init__(self, properties): values = np.array(hist['values']) else: # Load the values from a file - values = np.genfromtxt(hist['values']['filename'], delimiter=',') + filename = hist['values']['filename'] + if not isabs(filename): + filename = join(directory, filename) + values = np.genfromtxt(filename, delimiter=',') time_history = TimeHistory( time=Q_(values[:, time_col], time_units), From 5e31e4f267f2334b80f4f3e2acfa78a0a3e47eec Mon Sep 17 00:00:00 2001 From: jsantner Date: Thu, 14 Jun 2018 14:59:01 -0700 Subject: [PATCH 08/15] Fix case for reading from dictionary --- pyked/chemked.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyked/chemked.py b/pyked/chemked.py index 0aae09a..f26bfab 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -111,8 +111,10 @@ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): if yaml_file is not None: with open(yaml_file, 'r') as f: self._properties = yaml.safe_load(f) + directory = dirname(yaml_file) elif dict_input is not None: self._properties = dict_input + directory = None else: raise NameError("ChemKED needs either a YAML filename or dictionary as input.") @@ -121,7 +123,7 @@ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): self.datapoints = [] for point in self._properties['datapoints']: - self.datapoints.append(DataPoint(point, dirname(yaml_file))) + self.datapoints.append(DataPoint(point, directory)) self.reference = Reference( volume=self._properties['reference'].get('volume'), From a7976a946711cfa5b678415b14c2dcd5f264a5d3 Mon Sep 17 00:00:00 2001 From: jsantner Date: Tue, 26 Jun 2018 09:42:51 -0700 Subject: [PATCH 09/15] Default directory is empty string to remove error possibility. Ask for forgiveness instead of permission when looking for csv file --- pyked/chemked.py | 2 +- pyked/validation.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyked/chemked.py b/pyked/chemked.py index f26bfab..862af78 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -634,7 +634,7 @@ class DataPoint(object): 'compression-ratio' ] - def __init__(self, properties, directory=None): + def __init__(self, properties, directory=''): for prop in self.value_unit_props: if prop in properties: quant = self.process_quantity(properties[prop]) diff --git a/pyked/validation.py b/pyked/validation.py index 3c62c4b..17fd016 100644 --- a/pyked/validation.py +++ b/pyked/validation.py @@ -251,11 +251,13 @@ def _validate_isvalid_history(self, isvalid_history, field, value): self._error(field, 'incompatible units; should be consistent ' 'with ' + property_units['time']) - # Check that the values have the right number of columns - # If reading from a file, the file will not be validated. - # A file can have an arbitrary number of columns, and the columns - # to be used are specified. - if type(value['values']) is list: + try: + if 'filename' not in value['values'].keys(): + self._error(field, 'must include filename or list of values') + # If reading from a file, the file will not be validated. + # A file can have an arbitrary number of columns, and the columns + # to be used are specified. + except AttributeError: n_cols = len(value['values'][0]) max_cols = max(value['time']['column'], value['quantity']['column'], @@ -264,8 +266,6 @@ def _validate_isvalid_history(self, isvalid_history, field, value): self._error(field, 'too many columns in the values') elif n_cols < max_cols: self._error(field, 'not enough columns in the values') - elif 'filename' not in value['values'].keys(): - self._error(field, 'must include filename or list of values') def _validate_isvalid_quantity(self, isvalid_quantity, field, value): """Checks for valid given value and appropriate units. From 52243bd97ef0988221310a3969b548c398edfd65 Mon Sep 17 00:00:00 2001 From: jsantner Date: Wed, 27 Jun 2018 08:52:32 -0700 Subject: [PATCH 10/15] directory is empty string for dictionary input --- pyked/chemked.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyked/chemked.py b/pyked/chemked.py index 862af78..4945191 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -114,7 +114,7 @@ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): directory = dirname(yaml_file) elif dict_input is not None: self._properties = dict_input - directory = None + directory = '' else: raise NameError("ChemKED needs either a YAML filename or dictionary as input.") From 26ccdd680f58cf877da31da567d04db1752c48fb Mon Sep 17 00:00:00 2001 From: jsantner Date: Wed, 27 Jun 2018 11:35:40 -0700 Subject: [PATCH 11/15] Requested changes. Revert DataPoint to its original form, use Path objects to indicate location of csv file. Still need to add tests for 100% coverage. --- pyked/chemked.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/pyked/chemked.py b/pyked/chemked.py index 4945191..b83a640 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -2,13 +2,16 @@ Main ChemKED module """ # Standard libraries -from os.path import exists, isabs, dirname, join +from os.path import exists from collections import namedtuple from warnings import warn from copy import deepcopy import xml.etree.ElementTree as etree import xml.dom.minidom as minidom from itertools import chain +from pathlib import Path +from distutils.version import LooseVersion +import sys import numpy as np @@ -78,6 +81,13 @@ Composition.amount.__doc__ = '(`~pint.Quantity`) The amount of this species' +if LooseVersion(sys.version) < '3.6': + # Allow old version of python to open Path objects. + oldopen = open + def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None): + return oldopen(str(file), mode, buffering, encoding, errors, newline, closefd, opener) + + class ChemKED(object): """Main ChemKED class. @@ -109,12 +119,11 @@ class ChemKED(object): """ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): if yaml_file is not None: + yaml_file = Path(yaml_file) with open(yaml_file, 'r') as f: self._properties = yaml.safe_load(f) - directory = dirname(yaml_file) elif dict_input is not None: self._properties = dict_input - directory = '' else: raise NameError("ChemKED needs either a YAML filename or dictionary as input.") @@ -123,7 +132,18 @@ def __init__(self, yaml_file=None, dict_input=None, *, skip_validation=False): self.datapoints = [] for point in self._properties['datapoints']: - self.datapoints.append(DataPoint(point, directory)) + if 'time-histories' in point: + for th in point['time-histories']: + try: + filename = Path(th['values']['filename']) + except TypeError: + pass + else: + if yaml_file is not None: + th['values']['filename'] = (yaml_file.parent / filename).resolve() + else: + th['values']['filename'] = filename.resolve() + self.datapoints.append(DataPoint(point)) self.reference = Reference( volume=self._properties['reference'].get('volume'), @@ -591,7 +611,6 @@ class DataPoint(object): Arguments: properties (`dict`): Dictionary adhering to the ChemKED format for ``datapoints`` - directory (`str`, optional): Directory to look for auxiliary files Attributes: composition (`list`): List of dictionaries representing the species and their quantities @@ -634,7 +653,7 @@ class DataPoint(object): 'compression-ratio' ] - def __init__(self, properties, directory=''): + def __init__(self, properties): for prop in self.value_unit_props: if prop in properties: quant = self.process_quantity(properties[prop]) @@ -689,9 +708,7 @@ def __init__(self, properties, directory=''): else: # Load the values from a file filename = hist['values']['filename'] - if not isabs(filename): - filename = join(directory, filename) - values = np.genfromtxt(filename, delimiter=',') + values = np.genfromtxt(hist['values']['filename'], delimiter=',') time_history = TimeHistory( time=Q_(values[:, time_col], time_units), From fc124a9628cbe9e54cfefbbdafcc1aabab7b77f9 Mon Sep 17 00:00:00 2001 From: jsantner Date: Wed, 27 Jun 2018 11:48:34 -0700 Subject: [PATCH 12/15] Fix PEP8 problems, still have to update tests --- pyked/chemked.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyked/chemked.py b/pyked/chemked.py index b83a640..f1cd797 100644 --- a/pyked/chemked.py +++ b/pyked/chemked.py @@ -84,7 +84,9 @@ if LooseVersion(sys.version) < '3.6': # Allow old version of python to open Path objects. oldopen = open - def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None): + + def open(file, mode='r', buffering=-1, encoding=None, errors=None, + newline=None, closefd=True, opener=None): return oldopen(str(file), mode, buffering, encoding, errors, newline, closefd, opener) @@ -707,7 +709,6 @@ def __init__(self, properties): values = np.array(hist['values']) else: # Load the values from a file - filename = hist['values']['filename'] values = np.genfromtxt(hist['values']['filename'], delimiter=',') time_history = TimeHistory( From d023bc0290e7ddffd07c90ec89904d5ba68c6613 Mon Sep 17 00:00:00 2001 From: jsantner Date: Wed, 27 Jun 2018 15:07:14 -0700 Subject: [PATCH 13/15] Add tests for csv file time history --- pyked/tests/test_chemked.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pyked/tests/test_chemked.py b/pyked/tests/test_chemked.py index 8a1561f..a8d6519 100644 --- a/pyked/tests/test_chemked.py +++ b/pyked/tests/test_chemked.py @@ -96,6 +96,28 @@ def test_missing_input(self, capfd): with pytest.raises(ValueError): ChemKED(dict_input=properties) + def test_csv_time_history(self): + file_path = os.path.join('testfile_rcm3.yaml') + filename = pkg_resources.resource_filename(__name__, file_path) + csv_filename = pkg_resources.resource_filename(__name__, 'rcm_history.csv') + from_file = ChemKED(filename) + + with open(filename, 'r') as f: + properties = yaml.safe_load(f) + properties['datapoints'][0]['time-histories'][0]['values']['filename'] = csv_filename + from_dict = ChemKED(dict_input=properties) + + for exp in [from_file, from_dict]: + exp_fname = exp._properties['datapoints'][0]['time-histories'][0]['values']['filename'] + assert(str(exp_fname) == csv_filename) + + # Test case where 'values' is a dict, but doesn't contain 'filename' + properties['datapoints'][0]['time-histories'][0]['values']['x'] = ( + properties['datapoints'][0]['time-histories'][0]['values'].pop(['filename'])) + with pytest.raises(NotImplementedError): + ChemKED(properties) + + class TestDataFrameOutput(object): """ From d91b4f1899dcee4beb8bd3b6a13149789e433c44 Mon Sep 17 00:00:00 2001 From: jsantner Date: Wed, 27 Jun 2018 15:15:13 -0700 Subject: [PATCH 14/15] Simpler way to test for missing filename, shouldn't create errors either. --- pyked/tests/test_chemked.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyked/tests/test_chemked.py b/pyked/tests/test_chemked.py index a8d6519..95c773a 100644 --- a/pyked/tests/test_chemked.py +++ b/pyked/tests/test_chemked.py @@ -112,8 +112,7 @@ def test_csv_time_history(self): assert(str(exp_fname) == csv_filename) # Test case where 'values' is a dict, but doesn't contain 'filename' - properties['datapoints'][0]['time-histories'][0]['values']['x'] = ( - properties['datapoints'][0]['time-histories'][0]['values'].pop(['filename'])) + del properties['datapoints'][0]['time-histories'][0]['values']['filename'] with pytest.raises(NotImplementedError): ChemKED(properties) From af3145cf90cfadf29f87c4a3b63b2c991633739c Mon Sep 17 00:00:00 2001 From: jsantner Date: Wed, 27 Jun 2018 15:23:52 -0700 Subject: [PATCH 15/15] Fixing minor mistake --- pyked/tests/test_chemked.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyked/tests/test_chemked.py b/pyked/tests/test_chemked.py index 95c773a..1e5d004 100644 --- a/pyked/tests/test_chemked.py +++ b/pyked/tests/test_chemked.py @@ -114,7 +114,7 @@ def test_csv_time_history(self): # Test case where 'values' is a dict, but doesn't contain 'filename' del properties['datapoints'][0]['time-histories'][0]['values']['filename'] with pytest.raises(NotImplementedError): - ChemKED(properties) + ChemKED(dict_input=properties)