1""" 2Test module for chemked.py 3""" 4# Standard libraries 5import os 6import pkg_resources 7import warnings 8from tempfile import TemporaryDirectory 9import xml.etree.ElementTree as etree 10from copy import deepcopy 11 12# Third-party libraries 13import numpy as np 14import pytest 15 16# Local imports 17from ..validation import schema, OurValidator, yaml, Q_ 18from ..chemked import ChemKED, DataPoint, Composition 19from ..converters import get_datapoints, get_common_properties 20from .._version import __version__ 21 22schema['chemked-version']['allowed'].append(__version__) 23 24warnings.simplefilter('always') 25 26 27class TestChemKED(object): 28 """ 29 """ 30 def test_create_chemked(self): 31 file_path = os.path.join('testfile_st.yaml') 32 filename = pkg_resources.resource_filename(__name__, file_path) 33 ChemKED(filename) 34 35 def test_skip_validation(self): 36 file_path = os.path.join('testfile_bad.yaml') 37 filename = pkg_resources.resource_filename(__name__, file_path) 38 ChemKED(filename, skip_validation=True) 39 40 def test_datapoints(self): 41 file_path = os.path.join('testfile_st.yaml') 42 filename = pkg_resources.resource_filename(__name__, file_path) 43 c = ChemKED(filename) 44 assert len(c.datapoints) == 5 45 46 temperatures = Q_([1164.48, 1164.97, 1264.2, 1332.57, 1519.18], 'K') 47 ignition_delays = Q_([471.54, 448.03, 291.57, 205.93, 88.11], 'us') 48 49 for i, d in enumerate(c.datapoints): 50 assert np.isclose(d.ignition_delay, ignition_delays[i]) 51 assert np.isclose(d.pressure, Q_(220., 'kPa')) 52 assert np.isclose(d.temperature, temperatures[i]) 53 assert d.pressure_rise is None 54 assert d.volume_history is None 55 assert d.rcm_data is None 56 assert d.ignition_type['type'] == 'd/dt max' 57 assert d.ignition_type['target'] == 'pressure' 58 59 def test_no_input(self): 60 """Test that no input raises an exception 61 """ 62 with pytest.raises(NameError): 63 ChemKED() 64 65 def test_dict_input(self): 66 file_path = os.path.join('testfile_required.yaml') 67 filename = pkg_resources.resource_filename(__name__, file_path) 68 with open(filename, 'r') as f: 69 properties = yaml.safe_load(f) 70 71 ChemKED(dict_input=properties) 72 73 def test_unallowed_input(self, capfd): 74 file_path = os.path.join('testfile_required.yaml') 75 filename = pkg_resources.resource_filename(__name__, file_path) 76 with open(filename, 'r') as f: 77 properties = yaml.safe_load(f) 78 79 properties['experiment-type'] = 'Ignition Delay' # should be 'ignition delay' 80 81 with pytest.raises(ValueError): 82 ChemKED(dict_input=properties) 83 84 out, err = capfd.readouterr() 85 assert out == ("experiment-type has an illegal value. Allowed values are ['ignition " 86 "delay'] and are case sensitive.\n") 87 88 def test_missing_input(self, capfd): 89 file_path = os.path.join('testfile_required.yaml') 90 filename = pkg_resources.resource_filename(__name__, file_path) 91 with open(filename, 'r') as f: 92 properties = yaml.safe_load(f) 93 94 properties.pop('apparatus') 95 96 with pytest.raises(ValueError): 97 ChemKED(dict_input=properties) 98 99 100class TestDataFrameOutput(object): 101 """ 102 """ 103 @pytest.fixture(scope='session') 104 def pd(self): 105 return pytest.importorskip('pandas') 106 107 @pytest.fixture(scope='session') 108 def pdt(self): 109 return pytest.importorskip('pandas.util.testing') 110 111 def test_get_dataframe(self, pd, pdt): 112 yaml_file = os.path.join('testfile_st.yaml') 113 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 114 c = ChemKED(yaml_filename).get_dataframe() 115 csv_file = os.path.join('dataframe_st.csv') 116 csv_filename = pkg_resources.resource_filename(__name__, csv_file) 117 converters = { 118 'Ignition Delay': Q_, 119 'Temperature': Q_, 120 'Pressure': Q_, 121 'H2': Q_, 122 'Ar': Q_, 123 'O2': Q_, 124 } 125 df = pd.read_csv(csv_filename, index_col=0, converters=converters) 126 pdt.assert_frame_equal(c.sort_index(axis=1), df.sort_index(axis=1), check_names=True) 127 128 def test_custom_dataframe(self, pd, pdt): 129 yaml_file = os.path.join('testfile_st.yaml') 130 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 131 cols_to_get = ['composition', 'Reference', 'apparatus', 'temperature', 'ignition delay'] 132 c = ChemKED(yaml_filename).get_dataframe(cols_to_get) 133 csv_file = os.path.join('dataframe_st.csv') 134 csv_filename = pkg_resources.resource_filename(__name__, csv_file) 135 converters = { 136 'Ignition Delay': Q_, 137 'Temperature': Q_, 138 'Pressure': Q_, 139 'H2': Q_, 140 'Ar': Q_, 141 'O2': Q_, 142 } 143 use_cols = ['Apparatus:Kind', 'Apparatus:Institution', 'Apparatus:Facility', 144 'Reference:Volume', 'Reference:Journal', 'Reference:Doi', 'Reference:Authors', 145 'Reference:Detail', 'Reference:Year', 'Reference:Pages', 'Temperature', 146 'Ignition Delay', 'H2', 'Ar', 'O2', 'Composition:Kind' 147 ] 148 df = pd.read_csv(csv_filename, converters=converters, usecols=use_cols) 149 pdt.assert_frame_equal(c.sort_index(axis=1), df.sort_index(axis=1), check_names=True) 150 151 def test_custom_dataframe_2(self, pd, pdt): 152 yaml_file = os.path.join('testfile_st.yaml') 153 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 154 cols_to_get = ['temperature', 'ignition delay', 'Pressure'] 155 c = ChemKED(yaml_filename).get_dataframe(cols_to_get) 156 csv_file = os.path.join('dataframe_st.csv') 157 csv_filename = pkg_resources.resource_filename(__name__, csv_file) 158 converters = { 159 'Ignition Delay': Q_, 160 'Temperature': Q_, 161 'Pressure': Q_, 162 'H2': Q_, 163 'Ar': Q_, 164 'O2': Q_, 165 } 166 use_cols = ['Temperature', 'Ignition Delay', 'Pressure'] 167 df = pd.read_csv(csv_filename, converters=converters, usecols=use_cols) 168 pdt.assert_frame_equal(c.sort_index(axis=1), df.sort_index(axis=1), check_names=True) 169 170 def test_invalid_column(self, pd): 171 yaml_file = os.path.join('testfile_st.yaml') 172 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 173 with pytest.raises(ValueError): 174 ChemKED(yaml_filename).get_dataframe(['bad column']) 175 176 def test_many_species(self, pd): 177 yaml_file = os.path.join('testfile_many_species.yaml') 178 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 179 c = ChemKED(yaml_filename).get_dataframe() 180 assert c.iloc[0]['New-Species-1'] == Q_(0.0, 'dimensionless') 181 assert c.iloc[0]['New-Species-2'] == Q_(0.0, 'dimensionless') 182 assert c.iloc[1]['H2'] == Q_(0.0, 'dimensionless') 183 assert c.iloc[1]['O2'] == Q_(0.0, 'dimensionless') 184 185 186class TestWriteFile(object): 187 """ 188 """ 189 def test_file_exists(self): 190 """ 191 """ 192 yaml_file = 'testfile_st.yaml' 193 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 194 c = ChemKED(yaml_filename) 195 196 with pytest.raises(OSError): 197 c.write_file(yaml_filename) 198 199 def test_overwrite(self): 200 """ 201 """ 202 yaml_file = 'testfile_st.yaml' 203 yaml_filename = pkg_resources.resource_filename(__name__, yaml_file) 204 with open(yaml_filename, 'r') as f: 205 lines = f.readlines() 206 207 with TemporaryDirectory() as temp_dir: 208 newfile_path = os.path.join(temp_dir, 'testfile.yaml') 209 with open(newfile_path, 'w') as f: 210 f.writelines(lines) 211 c = ChemKED(newfile_path) 212 213 # Expected error 214 with pytest.raises(OSError): 215 c.write_file(newfile_path) 216 217 # Now successful 218 assert c.write_file(newfile_path, overwrite=True) is None 219 220 @pytest.mark.parametrize("filename", [ 221 'testfile_st.yaml', 'testfile_st2.yaml', 'testfile_rcm.yaml', 222 'testfile_required.yaml', 'testfile_uncertainty.yaml' 223 ]) 224 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 225 def test_write_files(self, filename): 226 """Test proper writing of ChemKED files. 227 """ 228 file_path = os.path.join(filename) 229 filename = pkg_resources.resource_filename(__name__, file_path) 230 c = ChemKED(filename) 231 232 with TemporaryDirectory() as temp_dir: 233 c.write_file(os.path.join(temp_dir, 'testfile.yaml')) 234 235 # Now read in the file 236 with open(os.path.join(temp_dir, 'testfile.yaml'), 'r') as f: 237 properties = yaml.safe_load(f) 238 239 assert properties == c._properties 240 241 242class TestConvertToReSpecTh(object): 243 """Tests for conversion of ChemKED to ReSpecTh 244 """ 245 @pytest.mark.parametrize('filename_ck', ['testfile_st.yaml', 'testfile_rcm.yaml']) 246 def test_conversion_to_respecth(self, filename_ck): 247 """Test proper conversion to ReSpecTh XML. 248 """ 249 file_path = os.path.join(filename_ck) 250 filename = pkg_resources.resource_filename(__name__, file_path) 251 c_true = ChemKED(filename) 252 253 with TemporaryDirectory() as temp_dir: 254 newfile = os.path.join(temp_dir, 'test.xml') 255 c_true.convert_to_ReSpecTh(newfile) 256 with pytest.warns(UserWarning) as record: 257 c = ChemKED.from_respecth(newfile) 258 259 m = str(record.pop(UserWarning).message) 260 assert m == 'Using DOI to obtain reference information, rather than preferredKey.' 261 262 assert c.file_authors[0]['name'] == c_true.file_authors[0]['name'] 263 264 assert c.reference.detail == 'Converted from ReSpecTh XML file {}'.format(os.path.split(newfile)[1]) 265 266 assert c.apparatus.kind == c_true.apparatus.kind 267 assert c.experiment_type == c_true.experiment_type 268 assert c.reference.doi == c_true.reference.doi 269 assert len(c.datapoints) == len(c_true.datapoints) 270 271 @pytest.mark.parametrize('history_type, unit', 272 [('volume', 'cm3'), ('temperature', 'K'), ('pressure', 'bar')]) 273 def test_time_history_conversion_to_respecth(self, history_type, unit): 274 """Test proper conversion to ReSpecTh XML with time histories. 275 """ 276 file_path = os.path.join('testfile_rcm.yaml') 277 filename = pkg_resources.resource_filename(__name__, file_path) 278 with open(filename, 'r') as yaml_file: 279 properties = yaml.safe_load(yaml_file) 280 properties['datapoints'][0]['time-histories'][0]['type'] = history_type 281 properties['datapoints'][0]['time-histories'][0]['quantity']['units'] = unit 282 c_true = ChemKED(dict_input=properties) 283 284 with TemporaryDirectory() as temp_dir: 285 newfile = os.path.join(temp_dir, 'test.xml') 286 c_true.convert_to_ReSpecTh(newfile) 287 with pytest.warns(UserWarning) as record: 288 c = ChemKED.from_respecth(newfile) 289 290 m = str(record.pop(UserWarning).message) 291 assert m == 'Using DOI to obtain reference information, rather than preferredKey.' 292 293 assert c.file_authors[0]['name'] == c_true.file_authors[0]['name'] 294 295 assert c.reference.detail == 'Converted from ReSpecTh XML file {}'.format(os.path.split(newfile)[1]) 296 297 assert c.apparatus.kind == c_true.apparatus.kind 298 assert c.experiment_type == c_true.experiment_type 299 assert c.reference.doi == c_true.reference.doi 300 assert len(c.datapoints) == len(c_true.datapoints) 301 assert getattr(c.datapoints[0], '{}_history'.format(history_type)) is not None 302 303 @pytest.mark.parametrize('history_type, unit', 304 zip(['piston position', 'light emission', 'OH emission', 'absorption'], 305 ['cm', 'dimensionless', 'dimensionless', 'dimensionless'])) 306 def test_time_history_conversion_to_respecth_unsupported(self, history_type, unit): 307 """Test proper conversion to ReSpecTh XML with unsupported time histories. 308 """ 309 file_path = os.path.join('testfile_rcm.yaml') 310 filename = pkg_resources.resource_filename(__name__, file_path) 311 with open(filename, 'r') as yaml_file: 312 properties = yaml.safe_load(yaml_file) 313 properties['datapoints'][0]['time-histories'][0]['type'] = history_type 314 properties['datapoints'][0]['time-histories'][0]['quantity']['units'] = unit 315 c_true = ChemKED(dict_input=properties) 316 with TemporaryDirectory() as temp_dir: 317 newfile = os.path.join(temp_dir, 'test.xml') 318 with pytest.warns(UserWarning) as record: 319 c_true.convert_to_ReSpecTh(newfile) 320 m = str(record.pop(UserWarning).message) 321 assert m == ('The time-history type {} is not supported by ReSpecTh for ' 322 'ignition delay experiments'.format(history_type)) 323 with pytest.warns(UserWarning) as record: 324 c = ChemKED.from_respecth(newfile) 325 326 m = str(record.pop(UserWarning).message) 327 assert m == 'Using DOI to obtain reference information, rather than preferredKey.' 328 329 assert c.file_authors[0]['name'] == c_true.file_authors[0]['name'] 330 331 assert c.reference.detail == 'Converted from ReSpecTh XML file {}'.format(os.path.split(newfile)[1]) 332 333 assert c.apparatus.kind == c_true.apparatus.kind 334 assert c.experiment_type == c_true.experiment_type 335 assert c.reference.doi == c_true.reference.doi 336 assert len(c.datapoints) == len(c_true.datapoints) 337 assert getattr(c.datapoints[0], '{}_history'.format(history_type.replace(' ', '_'))) is None 338 339 @pytest.mark.parametrize('experiment_type', [ 340 'Laminar flame speed measurement', 'Species profile measurement', 341 'Outlet concentration measurement', 'Burner stabilized flame speciation measurement', 342 'Jet-stirred reactor measurement', 'Reaction rate coefficient measurement' 343 ]) 344 def test_conversion_to_respecth_error(self, experiment_type): 345 """Test for conversion errors. 346 """ 347 file_path = os.path.join('testfile_st.yaml') 348 filename = pkg_resources.resource_filename(__name__, file_path) 349 c = ChemKED(filename) 350 351 c.experiment_type = experiment_type 352 353 with pytest.raises(NotImplementedError) as excinfo: 354 c.convert_to_ReSpecTh('test.xml') 355 assert 'Only ignition delay type supported for conversion.' in str(excinfo.value) 356 357 def test_conversion_datapoints_composition_missing_inchi(self): 358 """Test for appropriate handling of composition with missing InChI. 359 """ 360 file_path = os.path.join('testfile_st.yaml') 361 filename = pkg_resources.resource_filename(__name__, file_path) 362 c = ChemKED(filename) 363 364 for idx, dp in enumerate(c.datapoints): 365 c.datapoints[idx].composition = dict( 366 H2=Composition(**{'amount': Q_(0.1, 'dimensionless'), 'species_name': 'H2', 367 'InChI': None, 'SMILES': None, 'atomic_composition': None}), 368 O2=Composition(**{'amount': Q_(0.1, 'dimensionless'), 'species_name': 'O2', 369 'InChI': None, 'SMILES': None, 'atomic_composition': None}), 370 Ar=Composition(**{'amount': Q_(0.8, 'dimensionless'), 'species_name': 'Ar', 371 'InChI': None, 'SMILES': None, 'atomic_composition': None}) 372 ) 373 374 with TemporaryDirectory() as temp_dir: 375 newfile = os.path.join(temp_dir, 'test.xml') 376 c.convert_to_ReSpecTh(newfile) 377 tree = etree.parse(newfile) 378 root = tree.getroot() 379 380 with pytest.warns(UserWarning) as record: 381 common = get_common_properties(root) 382 messages = [str(record.pop(UserWarning).message) for i in range(3)] 383 assert 'Missing InChI for species H2' in messages 384 assert 'Missing InChI for species O2' in messages 385 assert 'Missing InChI for species Ar' in messages 386 assert len(common['composition']['species']) == 3 387 for spec in common['composition']['species']: 388 assert spec in [{'amount': [0.1], 'species-name': 'H2'}, 389 {'amount': [0.1], 'species-name': 'O2'}, 390 {'amount': [0.8], 'species-name': 'Ar'} 391 ] 392 393 def test_conversion_datapoints_different_composition(self): 394 """Test for appropriate handling of datapoints with different composition. 395 """ 396 file_path = os.path.join('testfile_st.yaml') 397 filename = pkg_resources.resource_filename(__name__, file_path) 398 c = ChemKED(filename) 399 400 c.datapoints[0].composition = {'H2': Composition(**{'InChI': '1S/H2/h1H', 401 'amount': Q_(0.1, 'dimensionless'), 402 'species_name': 'H2', 'SMILES': None, 'atomic_composition': None}), 403 'O2': Composition(**{'InChI': '1S/O2/c1-2', 404 'amount': Q_(0.1, 'dimensionless'), 405 'species_name': 'O2', 'SMILES': None, 'atomic_composition': None}), 406 'N2': Composition(**{'amount': Q_(0.8, 'dimensionless'), 407 'species_name': 'N2', 408 'SMILES': 'N#N', 'InChI': None, 'atomic_composition': None}) 409 } 410 411 with TemporaryDirectory() as temp_dir: 412 newfile = os.path.join(temp_dir, 'test.xml') 413 c.convert_to_ReSpecTh(newfile) 414 415 tree = etree.parse(newfile) 416 root = tree.getroot() 417 with pytest.warns(UserWarning) as record: 418 datapoints = get_datapoints(root) 419 m = str(record.pop(UserWarning).message) 420 assert m == 'Missing InChI for species N2' 421 422 assert len(datapoints[0]['composition']['species']) == 3 423 for spec in datapoints[0]['composition']['species']: 424 assert spec in [{'InChI': '1S/H2/h1H', 425 'amount': [0.1], 426 'species-name': 'H2'}, 427 {'InChI': '1S/O2/c1-2', 428 'amount': [0.1], 429 'species-name': 'O2'}, 430 {'amount': [0.8], 431 'species-name': 'N2', 432 'InChI': None} 433 ] 434 435 def test_conversion_error_datapoints_different_composition_type(self): 436 """Test for appropriate erorr of datapoints with different composition type. 437 """ 438 file_path = os.path.join('testfile_st.yaml') 439 filename = pkg_resources.resource_filename(__name__, file_path) 440 c = ChemKED(filename) 441 c.datapoints[0].composition_type = 'mass fraction' 442 443 with pytest.raises(NotImplementedError) as excinfo: 444 c.convert_to_ReSpecTh('test.xml') 445 assert ('Error: ReSpecTh does not support varying composition ' 446 'type among datapoints.') in str(excinfo.value) 447 448 def test_conversion_to_respecth_error_volume_history_datapoints(self): 449 """Test for error raised if RCM with multiple datapoints with volume history. 450 """ 451 file_path = os.path.join('testfile_rcm.yaml') 452 filename = pkg_resources.resource_filename(__name__, file_path) 453 c = ChemKED(filename) 454 455 # Repeat datapoint, such that two with volume histories 456 c.datapoints.append(c.datapoints[0]) 457 458 with pytest.raises(NotImplementedError) as excinfo: 459 c.convert_to_ReSpecTh('test.xml') 460 assert ('Error: ReSpecTh files do not support multiple datapoints with a ' 461 'time history.' in str(excinfo.value) 462 ) 463 464 @pytest.mark.parametrize('ignition_target', ['pressure', 'temperature', 'OH', 'CH', 'OH*', 'CH*']) 465 def test_conversion_to_respecth_ignition_targets(self, ignition_target): 466 """Test proper conversion for different ignition targets. 467 """ 468 file_path = os.path.join('testfile_st.yaml') 469 filename = pkg_resources.resource_filename(__name__, file_path) 470 c = ChemKED(filename) 471 472 for dp in c.datapoints: 473 dp.ignition_type['target'] = ignition_target 474 475 with TemporaryDirectory() as temp_dir: 476 newfile = os.path.join(temp_dir, 'test.xml') 477 c.convert_to_ReSpecTh(newfile) 478 479 tree = etree.parse(newfile) 480 root = tree.getroot() 481 elem = root.find('ignitionType') 482 elem = elem.attrib 483 484 if ignition_target == 'pressure': 485 assert elem['target'] == 'P' 486 elif ignition_target == 'temperature': 487 assert elem['target'] == 'T' 488 else: 489 assert elem['target'] == ignition_target 490 491 @pytest.mark.parametrize('ignition_type', ['d/dt max', 'max', '1/2 max', 'min', 'd/dt max extrapolated']) 492 def test_conversion_to_respecth_ignition_types(self, ignition_type): 493 """Test proper conversion for different ignition types. 494 """ 495 file_path = os.path.join('testfile_st.yaml') 496 filename = pkg_resources.resource_filename(__name__, file_path) 497 c = ChemKED(filename) 498 499 for dp in c.datapoints: 500 dp.ignition_type['type'] = ignition_type 501 502 with TemporaryDirectory() as temp_dir: 503 newfile = os.path.join(temp_dir, 'test.xml') 504 c.convert_to_ReSpecTh(newfile) 505 506 tree = etree.parse(newfile) 507 root = tree.getroot() 508 elem = root.find('ignitionType') 509 elem = elem.attrib 510 511 if ignition_type == 'd/dt max extrapolated': 512 assert elem['type'] == 'baseline max intercept from d/dt' 513 else: 514 assert elem['type'] == ignition_type 515 516 def test_conversion_multiple_ignition_targets(self): 517 """Test that multiple ignition targets for datapoints fails 518 """ 519 file_path = os.path.join('testfile_st.yaml') 520 filename = pkg_resources.resource_filename(__name__, file_path) 521 c = ChemKED(filename) 522 523 c.datapoints[0].ignition_type['target'] = 'temperature' 524 with TemporaryDirectory() as temp_dir: 525 newfile = os.path.join(temp_dir, 'test.xml') 526 with pytest.raises(NotImplementedError) as e: 527 c.convert_to_ReSpecTh(newfile) 528 529 assert ('Different ignition targets or types for multiple datapoints are not supported in ' 530 'ReSpecTh.' in str(e.value)) 531 532 533class TestDataPoint(object): 534 """ 535 """ 536 def load_properties(self, test_file): 537 file_path = os.path.join(test_file) 538 filename = pkg_resources.resource_filename(__name__, file_path) 539 with open(filename, 'r') as f: 540 properties = yaml.safe_load(f) 541 542 v = OurValidator(schema) 543 if not v.validate(properties): 544 raise ValueError(v.errors) 545 546 return properties['datapoints'] 547 548 def test_create_datapoint(self): 549 properties = self.load_properties('testfile_required.yaml') 550 DataPoint(properties[0]) 551 552 def test_cantera_unknown_composition_type(self): 553 properties = self.load_properties('testfile_required.yaml') 554 d = DataPoint(properties[0]) 555 d.composition_type = 'unknown type' 556 with pytest.raises(ValueError): 557 d.get_cantera_composition_string() 558 559 def test_cantera_composition_mole_fraction(self): 560 properties = self.load_properties('testfile_required.yaml') 561 d = DataPoint(properties[0]) 562 # The order of the keys should not change between calls provided the contents of the 563 # dictionary don't change. Therefore, spec_order should be the same order as the 564 # Cantera mole fraction string constructed in a loop in the code 565 comps = {'H2': 'H2:4.4400e-03', 'O2': 'O2:5.5600e-03', 'Ar': 'Ar:9.9000e-01'} 566 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 567 assert d.composition_type == 'mole fraction' 568 assert d.get_cantera_mole_fraction() == compare_str 569 570 def test_cantera_composition_mole_fraction_bad(self): 571 properties = self.load_properties('testfile_required.yaml') 572 d = DataPoint(properties[1]) 573 assert d.composition_type == 'mass fraction' 574 with pytest.raises(ValueError): 575 d.get_cantera_mole_fraction() 576 577 def test_cantera_composition_mass_fraction(self): 578 properties = self.load_properties('testfile_required.yaml') 579 d = DataPoint(properties[1]) 580 # The order of the keys should not change between calls provided the contents of the 581 # dictionary don't change. Therefore, spec_order should be the same order as the 582 # Cantera mole fraction string constructed in a loop in the code 583 comps = {'H2': 'H2:2.2525e-04', 'O2': 'O2:4.4775e-03', 'Ar': 'Ar:9.9530e-01'} 584 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 585 assert d.composition_type == 'mass fraction' 586 assert d.get_cantera_mass_fraction() == compare_str 587 588 def test_cantera_composition_mass_fraction_bad(self): 589 properties = self.load_properties('testfile_required.yaml') 590 d = DataPoint(properties[0]) 591 assert d.composition_type == 'mole fraction' 592 with pytest.raises(ValueError): 593 d.get_cantera_mass_fraction() 594 595 def test_cantera_composition_mole_percent(self): 596 properties = self.load_properties('testfile_required.yaml') 597 d = DataPoint(properties[2]) 598 # The order of the keys should not change between calls provided the contents of the 599 # dictionary don't change. Therefore, spec_order should be the same order as the 600 # Cantera mole fraction string constructed in a loop in the code 601 comps = {'H2': 'H2:4.4400e-03', 'O2': 'O2:5.5600e-03', 'Ar': 'Ar:9.9000e-01'} 602 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 603 assert d.composition_type == 'mole percent' 604 assert d.get_cantera_mole_fraction() == compare_str 605 606 def test_cantera_change_species_by_name_mole_fraction(self): 607 properties = self.load_properties('testfile_required.yaml') 608 d = DataPoint(properties[0]) 609 # The order of the keys should not change between calls provided the contents of the 610 # dictionary don't change. Therefore, spec_order should be the same order as the 611 # Cantera mole fraction string constructed in a loop in the code 612 comps = {'H2': 'h2:4.4400e-03', 'O2': 'o2:5.5600e-03', 'Ar': 'Ar:9.9000e-01'} 613 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 614 species_conversion = {'H2': 'h2', 'O2': 'o2'} 615 assert d.get_cantera_mole_fraction(species_conversion) == compare_str 616 617 def test_cantera_change_species_by_inchi_mole_fraction(self): 618 properties = self.load_properties('testfile_required.yaml') 619 d = DataPoint(properties[0]) 620 # The order of the keys should not change between calls provided the contents of the 621 # dictionary don't change. Therefore, spec_order should be the same order as the 622 # Cantera mole fraction string constructed in a loop in the code 623 comps = {'H2': 'h2:4.4400e-03', 'O2': 'o2:5.5600e-03', 'Ar': 'Ar:9.9000e-01'} 624 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 625 species_conversion = {'1S/H2/h1H': 'h2', '1S/O2/c1-2': 'o2'} 626 assert d.get_cantera_mole_fraction(species_conversion) == compare_str 627 628 def test_cantera_change_species_by_name_mole_percent(self): 629 properties = self.load_properties('testfile_required.yaml') 630 d = DataPoint(properties[2]) 631 # The order of the keys should not change between calls provided the contents of the 632 # dictionary don't change. Therefore, spec_order should be the same order as the 633 # Cantera mole fraction string constructed in a loop in the code 634 comps = {'H2': 'h2:4.4400e-03', 'O2': 'o2:5.5600e-03', 'Ar': 'Ar:9.9000e-01'} 635 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 636 species_conversion = {'H2': 'h2', 'O2': 'o2'} 637 assert d.get_cantera_mole_fraction(species_conversion) == compare_str 638 639 def test_cantera_change_species_by_inchi_mole_percent(self): 640 properties = self.load_properties('testfile_required.yaml') 641 d = DataPoint(properties[2]) 642 # The order of the keys should not change between calls provided the contents of the 643 # dictionary don't change. Therefore, spec_order should be the same order as the 644 # Cantera mole fraction string constructed in a loop in the code 645 comps = {'H2': 'h2:4.4400e-03', 'O2': 'o2:5.5600e-03', 'Ar': 'Ar:9.9000e-01'} 646 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 647 species_conversion = {'1S/H2/h1H': 'h2', '1S/O2/c1-2': 'o2'} 648 assert d.get_cantera_mole_fraction(species_conversion) == compare_str 649 650 def test_cantera_change_species_by_name_mass_fraction(self): 651 properties = self.load_properties('testfile_required.yaml') 652 d = DataPoint(properties[1]) 653 # The order of the keys should not change between calls provided the contents of the 654 # dictionary don't change. Therefore, spec_order should be the same order as the 655 # Cantera mole fraction string constructed in a loop in the code 656 comps = {'H2': 'h2:2.2525e-04', 'O2': 'o2:4.4775e-03', 'Ar': 'Ar:9.9530e-01'} 657 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 658 species_conversion = {'H2': 'h2', 'O2': 'o2'} 659 assert d.get_cantera_mass_fraction(species_conversion) == compare_str 660 661 def test_cantera_change_species_by_inchi_mass_fraction(self): 662 properties = self.load_properties('testfile_required.yaml') 663 d = DataPoint(properties[1]) 664 # The order of the keys should not change between calls provided the contents of the 665 # dictionary don't change. Therefore, spec_order should be the same order as the 666 # Cantera mole fraction string constructed in a loop in the code 667 comps = {'H2': 'h2:2.2525e-04', 'O2': 'o2:4.4775e-03', 'Ar': 'Ar:9.9530e-01'} 668 compare_str = ', '.join([comps[s] for s in d.composition.keys()]) 669 species_conversion = {'1S/H2/h1H': 'h2', '1S/O2/c1-2': 'o2'} 670 assert d.get_cantera_mass_fraction(species_conversion) == compare_str 671 672 def test_cantera_change_species_missing_mole_fraction(self): 673 properties = self.load_properties('testfile_required.yaml') 674 d = DataPoint(properties[0]) 675 species_conversion = {'this-does-not-exist': 'h2', 'O2': 'o2'} 676 with pytest.raises(ValueError): 677 d.get_cantera_mole_fraction(species_conversion) 678 679 def test_cantera_change_species_missing_mass_fraction(self): 680 properties = self.load_properties('testfile_required.yaml') 681 d = DataPoint(properties[1]) 682 species_conversion = {'this-does-not-exist': 'h2', 'O2': 'o2'} 683 with pytest.raises(ValueError): 684 d.get_cantera_mass_fraction(species_conversion) 685 686 def test_cantera_change_species_duplicate_mole_fraction(self): 687 properties = self.load_properties('testfile_required.yaml') 688 d = DataPoint(properties[0]) 689 species_conversion = {'H2': 'h2', '1S/H2/h1H': 'h2'} 690 with pytest.raises(ValueError): 691 d.get_cantera_mole_fraction(species_conversion) 692 693 def test_cantera_change_species_duplicate_mass_fraction(self): 694 properties = self.load_properties('testfile_required.yaml') 695 d = DataPoint(properties[1]) 696 species_conversion = {'H2': 'h2', '1S/H2/h1H': 'h2'} 697 with pytest.raises(ValueError): 698 d.get_cantera_mass_fraction(species_conversion) 699 700 def test_composition(self): 701 properties = self.load_properties('testfile_required.yaml') 702 d = DataPoint(properties[2]) 703 assert len(d.composition) == 3 704 assert np.isclose(d.composition['H2'].amount, Q_(0.444)) 705 assert d.composition['H2'].species_name == 'H2' 706 assert np.isclose(d.composition['O2'].amount, Q_(0.556)) 707 assert d.composition['O2'].species_name == 'O2' 708 assert np.isclose(d.composition['Ar'].amount, Q_(99.0)) 709 assert d.composition['Ar'].species_name == 'Ar' 710 711 def test_ignition_delay(self): 712 properties = self.load_properties('testfile_required.yaml') 713 d = DataPoint(properties[0]) 714 assert np.isclose(d.ignition_delay, Q_(471.54, 'us')) 715 716 def test_first_stage_ignition_delay(self): 717 properties = self.load_properties('testfile_rcm2.yaml') 718 d = DataPoint(properties[0]) 719 assert np.isclose(d.first_stage_ignition_delay.value, Q_(0.5, 'ms')) 720 assert np.isclose(d.first_stage_ignition_delay.error, Q_(0.005, 'ms')) 721 722 def test_temperature(self): 723 properties = self.load_properties('testfile_required.yaml') 724 d = DataPoint(properties[0]) 725 assert np.isclose(d.temperature, Q_(1164.48, 'K')) 726 727 def test_rcm_data(self): 728 properties = self.load_properties('testfile_rcm2.yaml') 729 d = DataPoint(properties[0]) 730 assert np.isclose(d.rcm_data.compression_time, Q_(38.0, 'ms')) 731 assert np.isclose(d.rcm_data.compressed_temperature.value, Q_(765, 'K')) 732 assert np.isclose(d.rcm_data.compressed_temperature.error, Q_(7.65, 'K')) 733 assert np.isclose(d.rcm_data.compressed_pressure, Q_(7.1, 'bar')) 734 assert np.isclose(d.rcm_data.stroke, Q_(10.0, 'inch')) 735 assert np.isclose(d.rcm_data.clearance, Q_(2.5, 'cm')) 736 assert np.isclose(d.rcm_data.compression_ratio, Q_(12.0, 'dimensionless')) 737 738 def test_pressure(self): 739 properties = self.load_properties('testfile_required.yaml') 740 d = DataPoint(properties[0]) 741 assert np.isclose(d.pressure, Q_(220.0, 'kPa')) 742 743 def test_pressure_rise(self): 744 properties = self.load_properties('testfile_st2.yaml') 745 d = DataPoint(properties[0]) 746 assert np.isclose(d.pressure_rise, Q_(0.1, '1/ms')) 747 748 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 749 def test_absolute_sym_uncertainty(self): 750 properties = self.load_properties('testfile_uncertainty.yaml') 751 d = DataPoint(properties[0]) 752 assert np.isclose(d.temperature.value, Q_(1164.48, 'K')) 753 assert np.isclose(d.temperature.error, Q_(10, 'K')) 754 755 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 756 def test_absolute_sym_comp_uncertainty(self): 757 properties = self.load_properties('testfile_uncertainty.yaml') 758 d = DataPoint(properties[0]) 759 assert np.isclose(d.composition['O2'].amount.value, Q_(0.556)) 760 assert np.isclose(d.composition['O2'].amount.error, Q_(0.002)) 761 762 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 763 def test_relative_sym_uncertainty(self): 764 properties = self.load_properties('testfile_uncertainty.yaml') 765 d = DataPoint(properties[1]) 766 assert np.isclose(d.ignition_delay.value, Q_(471.54, 'us')) 767 assert np.isclose(d.ignition_delay.error, Q_(47.154, 'us')) 768 assert np.isclose(d.ignition_delay.rel, 0.1) 769 770 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 771 def test_relative_sym_comp_uncertainty(self): 772 properties = self.load_properties('testfile_uncertainty.yaml') 773 d = DataPoint(properties[0]) 774 assert np.isclose(d.composition['H2'].amount.value, Q_(0.444)) 775 assert np.isclose(d.composition['H2'].amount.error, Q_(0.00444)) 776 assert np.isclose(d.composition['H2'].amount.rel, 0.01) 777 778 def test_absolute_asym_uncertainty(self): 779 properties = self.load_properties('testfile_uncertainty.yaml') 780 with pytest.warns(UserWarning) as record: 781 d = DataPoint(properties[2]) 782 m = str(record.pop(UserWarning).message) 783 assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 784 'and upper-uncertainty has been used as the symmetric uncertainty.') 785 assert np.isclose(d.temperature.value, Q_(1164.48, 'K')) 786 assert np.isclose(d.temperature.error, Q_(10, 'K')) 787 assert np.isclose(d.ignition_delay.value, Q_(471.54, 'us')) 788 assert np.isclose(d.ignition_delay.error, Q_(10, 'us')) 789 790 def test_relative_asym_uncertainty(self): 791 properties = self.load_properties('testfile_uncertainty.yaml') 792 with pytest.warns(UserWarning) as record: 793 d = DataPoint(properties[3]) 794 m = str(record.pop(UserWarning).message) 795 assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 796 'and upper-uncertainty has been used as the symmetric uncertainty.') 797 assert np.isclose(d.ignition_delay.value, Q_(471.54, 'us')) 798 assert np.isclose(d.ignition_delay.error, Q_(47.154, 'us')) 799 assert np.isclose(d.ignition_delay.rel, 0.1) 800 assert np.isclose(d.temperature.value, Q_(1164.48, 'K')) 801 assert np.isclose(d.temperature.error, Q_(116.448, 'K')) 802 assert np.isclose(d.temperature.rel, 0.1) 803 804 def test_absolute_asym_comp_uncertainty(self): 805 properties = self.load_properties('testfile_uncertainty.yaml') 806 with pytest.warns(UserWarning) as record: 807 d = DataPoint(properties[0]) 808 m = str(record.pop(UserWarning).message) 809 assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 810 'and upper-uncertainty has been used as the symmetric uncertainty.') 811 assert np.isclose(d.composition['Ar'].amount.value, Q_(99.0)) 812 assert np.isclose(d.composition['Ar'].amount.error, Q_(1.0)) 813 814 with pytest.warns(UserWarning) as record: 815 d = DataPoint(properties[1]) 816 m = str(record.pop(UserWarning).message) 817 assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 818 'and upper-uncertainty has been used as the symmetric uncertainty.') 819 assert np.isclose(d.composition['Ar'].amount.value, Q_(99.0)) 820 assert np.isclose(d.composition['Ar'].amount.error, Q_(1.0)) 821 822 def test_relative_asym_comp_uncertainty(self): 823 properties = self.load_properties('testfile_uncertainty.yaml') 824 with pytest.warns(UserWarning) as record: 825 d = DataPoint(properties[1]) 826 m = str(record.pop(UserWarning).message) 827 assert m == ('Asymmetric uncertainties are not supported. The maximum of lower-uncertainty ' 828 'and upper-uncertainty has been used as the symmetric uncertainty.') 829 assert np.isclose(d.composition['H2'].amount.value, Q_(0.444)) 830 assert np.isclose(d.composition['H2'].amount.error, Q_(0.0444)) 831 assert np.isclose(d.composition['H2'].amount.rel, 0.1) 832 833 assert np.isclose(d.composition['O2'].amount.value, Q_(0.556)) 834 assert np.isclose(d.composition['O2'].amount.error, Q_(0.0556)) 835 assert np.isclose(d.composition['O2'].amount.rel, 0.1) 836 837 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 838 def test_missing_uncertainty_parts(self): 839 properties = self.load_properties('testfile_uncertainty.yaml') 840 for prop in ['uncertainty', 'uncertainty-type']: 841 save = properties[0]['temperature'][1].pop(prop) 842 with pytest.raises(ValueError): 843 DataPoint(properties[0]) 844 properties[0]['temperature'][1][prop] = save 845 846 save = properties[1]['ignition-delay'][1].pop(prop) 847 with pytest.raises(ValueError): 848 DataPoint(properties[1]) 849 properties[1]['ignition-delay'][1][prop] = save 850 851 for prop in ['upper-uncertainty', 'lower-uncertainty']: 852 save = properties[2]['temperature'][1].pop(prop) 853 with pytest.raises(ValueError): 854 DataPoint(properties[2]) 855 properties[0]['temperature'][1][prop] = save 856 857 save = properties[3]['ignition-delay'][1].pop(prop) 858 with pytest.raises(ValueError): 859 DataPoint(properties[3]) 860 properties[1]['ignition-delay'][1][prop] = save 861 862 @pytest.mark.filterwarnings('ignore:Asymmetric uncertainties') 863 def test_missing_comp_uncertainty_parts(self): 864 properties = self.load_properties('testfile_uncertainty.yaml') 865 for prop in ['uncertainty', 'uncertainty-type']: 866 save = properties[0]['composition']['species'][0]['amount'][1].pop(prop) 867 with pytest.raises(ValueError): 868 DataPoint(properties[0]) 869 properties[0]['composition']['species'][0]['amount'][1][prop] = save 870 871 save = properties[0]['composition']['species'][1]['amount'][1].pop(prop) 872 with pytest.raises(ValueError): 873 DataPoint(properties[0]) 874 properties[0]['composition']['species'][1]['amount'][1][prop] = save 875 876 for prop in ['upper-uncertainty', 'lower-uncertainty']: 877 save = properties[0]['composition']['species'][2]['amount'][1].pop(prop) 878 with pytest.raises(ValueError): 879 DataPoint(properties[0]) 880 properties[0]['composition']['species'][2]['amount'][1][prop] = save 881 882 save = properties[1]['composition']['species'][2]['amount'][1].pop(prop) 883 with pytest.raises(ValueError): 884 DataPoint(properties[1]) 885 properties[1]['composition']['species'][2]['amount'][1][prop] = save 886 887 def test_volume_history(self): 888 """Test that volume history works properly. 889 890 Tests for deprecated code, to be removed after PyKED 0.4 891 """ 892 properties = self.load_properties('testfile_rcm_old.yaml') 893 with pytest.warns(DeprecationWarning) as record: 894 d = DataPoint(properties[0]) 895 m = str(record.pop(DeprecationWarning).message) 896 assert m == ('The volume-history field should be replaced by time-histories. ' 897 'volume-history will be removed after PyKED 0.4') 898 # Check other data group with volume history 899 np.testing.assert_allclose(d.volume_history.time, 900 Q_(np.arange(0, 9.7e-2, 1.e-3), 's') 901 ) 902 903 volumes = Q_(np.array([ 904 5.47669375000E+002, 5.46608789894E+002, 5.43427034574E+002, 905 5.38124109043E+002, 5.30700013298E+002, 5.21154747340E+002, 906 5.09488311170E+002, 4.95700704787E+002, 4.79791928191E+002, 907 4.61761981383E+002, 4.41610864362E+002, 4.20399162234E+002, 908 3.99187460106E+002, 3.77975757979E+002, 3.56764055851E+002, 909 3.35552353723E+002, 3.14340651596E+002, 2.93128949468E+002, 910 2.71917247340E+002, 2.50705545213E+002, 2.29493843085E+002, 911 2.08282140957E+002, 1.87070438830E+002, 1.65858736702E+002, 912 1.44647034574E+002, 1.23435332447E+002, 1.02223630319E+002, 913 8.10119281915E+001, 6.33355097518E+001, 5.27296586879E+001, 914 4.91943750000E+001, 4.97137623933E+001, 5.02063762048E+001, 915 5.06454851923E+001, 5.10218564529E+001, 5.13374097598E+001, 916 5.16004693977E+001, 5.18223244382E+001, 5.20148449242E+001, 917 5.21889350372E+001, 5.23536351113E+001, 5.25157124459E+001, 918 5.26796063730E+001, 5.28476160610E+001, 5.30202402028E+001, 919 5.31965961563E+001, 5.33748623839E+001, 5.35527022996E+001, 920 5.37276399831E+001, 5.38973687732E+001, 5.40599826225E+001, 921 5.42141273988E+001, 5.43590751578E+001, 5.44947289126E+001, 922 5.46215686913E+001, 5.47405518236E+001, 5.48529815402E+001, 923 5.49603582190E+001, 5.50642270863E+001, 5.51660349836E+001, 924 5.52670070646E+001, 5.53680520985E+001, 5.54697025392E+001, 925 5.55720927915E+001, 5.56749762728E+001, 5.57777790517E+001, 926 5.58796851466E+001, 5.59797461155E+001, 5.60770054561E+001, 927 5.61706266985E+001, 5.62600130036E+001, 5.63449057053E+001, 928 5.64254496625E+001, 5.65022146282E+001, 5.65761642150E+001, 929 5.66485675508E+001, 5.67208534842E+001, 5.67944133373E+001, 930 5.68703658198E+001, 5.69493069272E+001, 5.70310785669E+001, 931 5.71146023893E+001, 5.71978399741E+001, 5.72779572372E+001, 932 5.73517897984E+001, 5.74167271960E+001, 5.74721573687E+001, 933 5.75216388520E+001, 5.75759967785E+001, 5.76575701358E+001, 934 5.78058719368E+001, 5.80849611077E+001, 5.85928651155E+001, 935 5.94734357453E+001, 6.09310671165E+001, 6.32487551103E+001, 936 6.68100309742E+001 937 ]), 'cm**3') 938 np.testing.assert_allclose(d.volume_history.volume, volumes) 939 940 def test_time_and_volume_histories_error(self): 941 """Test that time-histories and volume-history together raise an error""" 942 properties = self.load_properties('testfile_rcm.yaml') 943 properties[0]['volume-history'] = {} 944 with pytest.raises(TypeError) as record: 945 DataPoint(properties[0]) 946 947 assert 'time-histories and volume-history are mutually exclusive' in str(record.value) 948 949 time_history_types = ['volume', 'temperature', 'pressure', 'piston_position', 950 'light_emission', 'OH_emission', 'absorption'] 951 952 @pytest.mark.parametrize('history_type', time_history_types) 953 def test_time_histories_array(self, history_type): 954 """Check that all of the history types are set properly""" 955 properties = self.load_properties('testfile_rcm.yaml') 956 properties[0]['time-histories'][0]['type'] = history_type 957 d = DataPoint(properties[0]) 958 959 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type)).time, 960 Q_(np.arange(0, 9.7e-2, 1.e-3), 's') 961 ) 962 963 quants = Q_(np.array([ 964 5.47669375000E+002, 5.46608789894E+002, 5.43427034574E+002, 965 5.38124109043E+002, 5.30700013298E+002, 5.21154747340E+002, 966 5.09488311170E+002, 4.95700704787E+002, 4.79791928191E+002, 967 4.61761981383E+002, 4.41610864362E+002, 4.20399162234E+002, 968 3.99187460106E+002, 3.77975757979E+002, 3.56764055851E+002, 969 3.35552353723E+002, 3.14340651596E+002, 2.93128949468E+002, 970 2.71917247340E+002, 2.50705545213E+002, 2.29493843085E+002, 971 2.08282140957E+002, 1.87070438830E+002, 1.65858736702E+002, 972 1.44647034574E+002, 1.23435332447E+002, 1.02223630319E+002, 973 8.10119281915E+001, 6.33355097518E+001, 5.27296586879E+001, 974 4.91943750000E+001, 4.97137623933E+001, 5.02063762048E+001, 975 5.06454851923E+001, 5.10218564529E+001, 5.13374097598E+001, 976 5.16004693977E+001, 5.18223244382E+001, 5.20148449242E+001, 977 5.21889350372E+001, 5.23536351113E+001, 5.25157124459E+001, 978 5.26796063730E+001, 5.28476160610E+001, 5.30202402028E+001, 979 5.31965961563E+001, 5.33748623839E+001, 5.35527022996E+001, 980 5.37276399831E+001, 5.38973687732E+001, 5.40599826225E+001, 981 5.42141273988E+001, 5.43590751578E+001, 5.44947289126E+001, 982 5.46215686913E+001, 5.47405518236E+001, 5.48529815402E+001, 983 5.49603582190E+001, 5.50642270863E+001, 5.51660349836E+001, 984 5.52670070646E+001, 5.53680520985E+001, 5.54697025392E+001, 985 5.55720927915E+001, 5.56749762728E+001, 5.57777790517E+001, 986 5.58796851466E+001, 5.59797461155E+001, 5.60770054561E+001, 987 5.61706266985E+001, 5.62600130036E+001, 5.63449057053E+001, 988 5.64254496625E+001, 5.65022146282E+001, 5.65761642150E+001, 989 5.66485675508E+001, 5.67208534842E+001, 5.67944133373E+001, 990 5.68703658198E+001, 5.69493069272E+001, 5.70310785669E+001, 991 5.71146023893E+001, 5.71978399741E+001, 5.72779572372E+001, 992 5.73517897984E+001, 5.74167271960E+001, 5.74721573687E+001, 993 5.75216388520E+001, 5.75759967785E+001, 5.76575701358E+001, 994 5.78058719368E+001, 5.80849611077E+001, 5.85928651155E+001, 995 5.94734357453E+001, 6.09310671165E+001, 6.32487551103E+001, 996 6.68100309742E+001 997 ]), 'cm**3') 998 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type)).quantity, quants) 999 assert all([getattr(d, '{}_history'.format(h)) is None for h in self.time_history_types if h != history_type]) 1000 1001 @pytest.mark.parametrize('history_type', time_history_types) 1002 def test_time_histories_file(self, history_type): 1003 """Check that all of the history types are set properly""" 1004 properties = self.load_properties('testfile_rcm.yaml') 1005 properties[0]['time-histories'][0]['type'] = history_type 1006 file_path = os.path.join('rcm_history.csv') 1007 filename = pkg_resources.resource_filename(__name__, file_path) 1008 properties[0]['time-histories'][0]['values'] = {'filename': filename} 1009 d = DataPoint(properties[0]) 1010 1011 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type)).time, 1012 Q_(np.arange(0, 9.7e-2, 1.e-3), 's') 1013 ) 1014 1015 quants = Q_(np.array([ 1016 5.47669375000E+002, 5.46608789894E+002, 5.43427034574E+002, 1017 5.38124109043E+002, 5.30700013298E+002, 5.21154747340E+002, 1018 5.09488311170E+002, 4.95700704787E+002, 4.79791928191E+002, 1019 4.61761981383E+002, 4.41610864362E+002, 4.20399162234E+002, 1020 3.99187460106E+002, 3.77975757979E+002, 3.56764055851E+002, 1021 3.35552353723E+002, 3.14340651596E+002, 2.93128949468E+002, 1022 2.71917247340E+002, 2.50705545213E+002, 2.29493843085E+002, 1023 2.08282140957E+002, 1.87070438830E+002, 1.65858736702E+002, 1024 1.44647034574E+002, 1.23435332447E+002, 1.02223630319E+002, 1025 8.10119281915E+001, 6.33355097518E+001, 5.27296586879E+001, 1026 4.91943750000E+001, 4.97137623933E+001, 5.02063762048E+001, 1027 5.06454851923E+001, 5.10218564529E+001, 5.13374097598E+001, 1028 5.16004693977E+001, 5.18223244382E+001, 5.20148449242E+001, 1029 5.21889350372E+001, 5.23536351113E+001, 5.25157124459E+001, 1030 5.26796063730E+001, 5.28476160610E+001, 5.30202402028E+001, 1031 5.31965961563E+001, 5.33748623839E+001, 5.35527022996E+001, 1032 5.37276399831E+001, 5.38973687732E+001, 5.40599826225E+001, 1033 5.42141273988E+001, 5.43590751578E+001, 5.44947289126E+001, 1034 5.46215686913E+001, 5.47405518236E+001, 5.48529815402E+001, 1035 5.49603582190E+001, 5.50642270863E+001, 5.51660349836E+001, 1036 5.52670070646E+001, 5.53680520985E+001, 5.54697025392E+001, 1037 5.55720927915E+001, 5.56749762728E+001, 5.57777790517E+001, 1038 5.58796851466E+001, 5.59797461155E+001, 5.60770054561E+001, 1039 5.61706266985E+001, 5.62600130036E+001, 5.63449057053E+001, 1040 5.64254496625E+001, 5.65022146282E+001, 5.65761642150E+001, 1041 5.66485675508E+001, 5.67208534842E+001, 5.67944133373E+001, 1042 5.68703658198E+001, 5.69493069272E+001, 5.70310785669E+001, 1043 5.71146023893E+001, 5.71978399741E+001, 5.72779572372E+001, 1044 5.73517897984E+001, 5.74167271960E+001, 5.74721573687E+001, 1045 5.75216388520E+001, 5.75759967785E+001, 5.76575701358E+001, 1046 5.78058719368E+001, 5.80849611077E+001, 5.85928651155E+001, 1047 5.94734357453E+001, 6.09310671165E+001, 6.32487551103E+001, 1048 6.68100309742E+001 1049 ]), 'cm**3') 1050 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type)).quantity, quants) 1051 assert all([getattr(d, '{}_history'.format(h)) is None for h in self.time_history_types if h != history_type]) 1052 1053 @pytest.mark.parametrize('history_type', zip(time_history_types[:-1], time_history_types[1:])) 1054 def test_multiple_time_histories(self, history_type): 1055 """Check that multiple of the history types are set properly. 1056 1057 Note the units aren't correct for the history types, but that doesn't get checked here, it 1058 gets checked in the validation of the YAML file by Cerberus. 1059 """ 1060 properties = self.load_properties('testfile_rcm.yaml') 1061 properties[0]['time-histories'][0]['type'] = history_type[0] 1062 properties[0]['time-histories'].append(deepcopy(properties[0]['time-histories'][0])) 1063 properties[0]['time-histories'][1]['type'] = history_type[1] 1064 d = DataPoint(properties[0]) 1065 1066 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type[0])).time, 1067 Q_(np.arange(0, 9.7e-2, 1.e-3), 's')) 1068 1069 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type[1])).time, 1070 Q_(np.arange(0, 9.7e-2, 1.e-3), 's')) 1071 1072 quants = Q_(np.array([ 1073 5.47669375000E+002, 5.46608789894E+002, 5.43427034574E+002, 1074 5.38124109043E+002, 5.30700013298E+002, 5.21154747340E+002, 1075 5.09488311170E+002, 4.95700704787E+002, 4.79791928191E+002, 1076 4.61761981383E+002, 4.41610864362E+002, 4.20399162234E+002, 1077 3.99187460106E+002, 3.77975757979E+002, 3.56764055851E+002, 1078 3.35552353723E+002, 3.14340651596E+002, 2.93128949468E+002, 1079 2.71917247340E+002, 2.50705545213E+002, 2.29493843085E+002, 1080 2.08282140957E+002, 1.87070438830E+002, 1.65858736702E+002, 1081 1.44647034574E+002, 1.23435332447E+002, 1.02223630319E+002, 1082 8.10119281915E+001, 6.33355097518E+001, 5.27296586879E+001, 1083 4.91943750000E+001, 4.97137623933E+001, 5.02063762048E+001, 1084 5.06454851923E+001, 5.10218564529E+001, 5.13374097598E+001, 1085 5.16004693977E+001, 5.18223244382E+001, 5.20148449242E+001, 1086 5.21889350372E+001, 5.23536351113E+001, 5.25157124459E+001, 1087 5.26796063730E+001, 5.28476160610E+001, 5.30202402028E+001, 1088 5.31965961563E+001, 5.33748623839E+001, 5.35527022996E+001, 1089 5.37276399831E+001, 5.38973687732E+001, 5.40599826225E+001, 1090 5.42141273988E+001, 5.43590751578E+001, 5.44947289126E+001, 1091 5.46215686913E+001, 5.47405518236E+001, 5.48529815402E+001, 1092 5.49603582190E+001, 5.50642270863E+001, 5.51660349836E+001, 1093 5.52670070646E+001, 5.53680520985E+001, 5.54697025392E+001, 1094 5.55720927915E+001, 5.56749762728E+001, 5.57777790517E+001, 1095 5.58796851466E+001, 5.59797461155E+001, 5.60770054561E+001, 1096 5.61706266985E+001, 5.62600130036E+001, 5.63449057053E+001, 1097 5.64254496625E+001, 5.65022146282E+001, 5.65761642150E+001, 1098 5.66485675508E+001, 5.67208534842E+001, 5.67944133373E+001, 1099 5.68703658198E+001, 5.69493069272E+001, 5.70310785669E+001, 1100 5.71146023893E+001, 5.71978399741E+001, 5.72779572372E+001, 1101 5.73517897984E+001, 5.74167271960E+001, 5.74721573687E+001, 1102 5.75216388520E+001, 5.75759967785E+001, 5.76575701358E+001, 1103 5.78058719368E+001, 5.80849611077E+001, 5.85928651155E+001, 1104 5.94734357453E+001, 6.09310671165E+001, 6.32487551103E+001, 1105 6.68100309742E+001 1106 ]), 'cm**3') 1107 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type[0])).quantity, quants) 1108 np.testing.assert_allclose(getattr(d, '{}_history'.format(history_type[1])).quantity, quants) 1109 assert all([getattr(d, '{}_history'.format(h)) is None for h in self.time_history_types if h not in history_type]) 1110 1111 @pytest.mark.parametrize('history_type', zip(time_history_types, time_history_types)) 1112 def test_duplicate_time_histories(self, history_type): 1113 """Check that duplicates of the history types raise an error""" 1114 properties = self.load_properties('testfile_rcm.yaml') 1115 properties[0]['time-histories'][0]['type'] = history_type[0] 1116 properties[0]['time-histories'].append(deepcopy(properties[0]['time-histories'][0])) 1117 properties[0]['time-histories'][1]['type'] = history_type[1] 1118 with pytest.raises(ValueError) as record: 1119 DataPoint(properties[0]) 1120 assert ('Each history type may only be specified once. {} was ' 1121 'specified multiple times'.format(history_type[0])) in str(record.value) 1122 1123 def test_supported_ignition_types(self): 1124 # pressure d/dt max 1125 properties = self.load_properties('testfile_st.yaml') 1126 datapoints = [DataPoint(d) for d in properties] 1127 for d in datapoints: 1128 assert d.ignition_type['target'] == 'pressure' 1129 assert d.ignition_type['type'] == 'd/dt max' 1130 1131 # OH, max 1132 properties = self.load_properties('testfile_st2.yaml') 1133 datapoints = [DataPoint(d) for d in properties] 1134 for d in datapoints: 1135 assert d.ignition_type['target'] == 'OH' 1136 assert d.ignition_type['type'] == 'max' 1137 1138 # OH*, 1/2 max 1139 properties = self.load_properties('testfile_st_p5.yaml') 1140 datapoints = [DataPoint(d) for d in properties] 1141 for d in datapoints: 1142 assert d.ignition_type['target'] == 'OH*' 1143 assert d.ignition_type['type'] == '1/2 max' 1144 1145 # CH, min 1146 properties = self.load_properties('testfile_required.yaml') 1147 datapoints = [DataPoint(d) for d in properties] 1148 assert datapoints[0].ignition_type['target'] == 'CH' 1149 assert datapoints[0].ignition_type['type'] == 'min' 1150 1151 # CH*, d/dt max extrapolated 1152 assert datapoints[1].ignition_type['target'] == 'CH*' 1153 assert datapoints[1].ignition_type['type'] == 'd/dt max extrapolated' 1154 1155 def test_changing_ignition_type(self): 1156 properties = self.load_properties('testfile_st.yaml') 1157 datapoints = [DataPoint(d) for d in properties] 1158 datapoints[0].ignition_type['target'] = 'temperature' 1159 assert datapoints[0].ignition_type['target'] == 'temperature' 1160 for d in datapoints[1:]: 1161 assert d.ignition_type['target'] == 'pressure' 1162