def test_level_of_theory_consistency(self): """ Test that ErrorCancelingReaction objects properly check that all species use the same level of theory """ # Take ethane as the target ethane = ErrorCancelingSpecies(self.molecule1, (100.0, 'kJ/mol'), LevelOfTheory('test_A')) methyl = ErrorCancelingSpecies(self.molecule2, (20.0, 'kcal/mol'), LevelOfTheory('test_B'), (21000.0, 'cal/mol')) # This should throw an exception because the model chemistry is different with self.assertRaises(ValueError): ErrorCancelingReaction(ethane, {methyl: 2})
def test_molecule_input_in_error_canceling_species(self): """ Test that an exception is raised if an rmgpy Molecule object is not passed to an ErrorCancelingSpecies """ with self.assertRaises(ValueError): ErrorCancelingSpecies(self.species, (100.0, 'J/mol'), LevelOfTheory('test'))
def setUp(cls): """Preparation for all unit tests in this class.""" cls.directory = os.path.join( os.path.dirname(os.path.dirname(rmgpy.__file__)), 'examples', 'arkane') cls.level_of_theory = LevelOfTheory("cbs-qb3") cls.frequencyScaleFactor = 0.99 cls.useHinderedRotors = False cls.useBondCorrections = True
def test_to_model_chem(self): """ Test conversion to model chemistry. """ self.assertEqual(FREQ.to_model_chem(), FREQ_MODELCHEM) self.assertEqual(ENERGY.to_model_chem(), ENERGY_MODELCHEM) lot = LevelOfTheory(method='CBS-QB3', software='g16') self.assertEqual(lot.to_model_chem(), 'cbsqb3')
def test_list_available_chemistry(self): """ Test that a set of available levels of theory can be return for the reference database """ level_of_theory_list = self.database.list_available_chemistry() self.assertIn( LevelOfTheory(method='wb97m-v', basis='def2-tzvpd', software='qchem'), level_of_theory_list)
def test_write_to_database(self): """ Test that results can be written to the database. """ # Check that error is raised when no energies are available self.ae.atom_energies = None with self.assertRaises(ValueError) as e: self.ae.write_to_database('test') self.assertIn('No atom energies', str(e.exception)) # Check that error is raised if energies already exist self.ae.atom_energies = {'H': 1.0, 'C': 2.0} tmp_datafile_fd, tmp_datafile_path = tempfile.mkstemp(suffix='.py') lot = LevelOfTheory(method='wb97m-v', basis='def2-tzvpd', software='Q-Chem') with self.assertRaises(ValueError) as e: self.ae.write_to_database(lot, alternate_path=tmp_datafile_path) self.assertIn('overwrite', str(e.exception)) # Dynamically set data file as module spec = importlib.util.spec_from_file_location( os.path.basename(tmp_datafile_path), tmp_datafile_path) module = importlib.util.module_from_spec(spec) # Check that existing energies can be overwritten self.ae.write_to_database(lot, overwrite=True, alternate_path=tmp_datafile_path) spec.loader.exec_module(module) # Load data as module self.assertEqual(self.ae.atom_energies, module.atom_energies[repr(lot)]) # Check that new energies can be written lot = LevelOfTheory('test') self.ae.write_to_database(lot, alternate_path=tmp_datafile_path) spec.loader.exec_module(module) # Reload data module self.assertEqual(self.ae.atom_energies, module.atom_energies[repr(lot)]) os.close(tmp_datafile_fd) os.remove(tmp_datafile_path)
def test_to_arkane(self): """Test converting Level to LevelOfTheory""" level_1 = Level(repr='wB97xd/def2-tzvp') self.assertEqual( level_1.to_arkane_level_of_theory(), LevelOfTheory(method='wb97xd', basis='def2tzvp', software='gaussian')) self.assertEqual( level_1.to_arkane_level_of_theory(variant='freq'), LevelOfTheory(method='wb97xd', basis='def2tzvp', software='gaussian')) level_2 = Level(repr='CBS-QB3') self.assertEqual(level_2.to_arkane_level_of_theory(), LevelOfTheory(method='cbs-qb3', software='gaussian')) self.assertEqual(level_2.to_arkane_level_of_theory(variant='AEC'), LevelOfTheory(method='cbs-qb3', software='gaussian')) self.assertEqual(level_2.to_arkane_level_of_theory(variant='freq'), LevelOfTheory(method='cbs-qb3', software='gaussian')) self.assertEqual(level_2.to_arkane_level_of_theory(variant='BAC'), LevelOfTheory(method='cbs-qb3', software='gaussian')) self.assertIsNone( level_2.to_arkane_level_of_theory( variant='BAC', bac_type='m')) # might change in the future level_3 = Level( repr={ 'method': 'DLPNO-CCSD(T)', 'basis': 'def2-TZVp', 'auxiliary_basis': 'def2-tzvp/c', 'solvation_method': 'SMD', 'solvent': 'water', 'solvation_scheme_level': 'APFD/def2-TZVp' }) self.assertEqual( level_3.to_arkane_level_of_theory(), LevelOfTheory(method='dlpno-ccsd(t)', basis='def2tzvp', software='orca')) self.assertEqual( level_3.to_arkane_level_of_theory(comprehensive=True), LevelOfTheory(method='dlpnoccsd(t)', basis='def2tzvp', auxiliary_basis='def2tzvp/c', solvent='water', solvation_method='smd', software='orca'))
def test_create_and_load_yaml(self): """ Test properly loading the ArkaneSpecies object and respective sub-objects """ # Create YAML file by running Arkane job_list = self.arkane.load_input_file(self.dump_input_path) for job in job_list: job.execute(output_directory=self.dump_path) # Load in newly created YAML file arkane_spc_old = job_list[0].arkane_species arkane_spc = ArkaneSpecies.__new__(ArkaneSpecies) arkane_spc.load_yaml( path=os.path.join(self.dump_path, 'species', arkane_spc_old.label + '.yml')) self.assertIsInstance(arkane_spc, ArkaneSpecies) # checks make_object self.assertIsInstance(arkane_spc.molecular_weight, ScalarQuantity) self.assertIsInstance(arkane_spc.thermo, NASA) self.assertNotEqual(arkane_spc.author, '') self.assertEqual(arkane_spc.inchi, 'InChI=1S/C2H6/c1-2/h1-2H3') self.assertEqual(arkane_spc.inchi_key, 'OTMSDBZUPAUEDD-UHFFFAOYSA-N') self.assertEqual(arkane_spc.smiles, 'CC') self.assertTrue('8 H u0 p0 c0 {2,S}' in arkane_spc.adjacency_list) self.assertEqual(arkane_spc.label, 'C2H6') self.assertEqual(arkane_spc.frequency_scale_factor, 0.99 * 1.014) # checks float conversion self.assertFalse(arkane_spc.use_bond_corrections) self.assertAlmostEqual( arkane_spc.conformer.modes[2].frequencies.value_si[0], 830.38202, 4) # HarmonicOsc. self.assertIsInstance(arkane_spc.energy_transfer_model, SingleExponentialDown) self.assertFalse(arkane_spc.is_ts) self.assertEqual(arkane_spc.level_of_theory, LevelOfTheory('cbs-qb3')) self.assertIsInstance(arkane_spc.thermo_data, ThermoData) self.assertTrue(arkane_spc.use_hindered_rotors) self.assertIsInstance(arkane_spc.chemkin_thermo_string, str) expected_xyz = """8 C2H6 C 0.00075400 0.00119300 0.00055200 H 0.00074000 0.00117100 1.09413800 H 1.04376600 0.00117100 -0.32820200 H -0.44760300 0.94289500 -0.32825300 C -0.76014200 -1.20389600 -0.55748300 H -0.76012800 -1.20387400 -1.65106900 H -0.31178500 -2.14559800 -0.22867800 H -1.80315400 -1.20387400 -0.22872900""" self.assertEqual(arkane_spc.xyz, expected_xyz)
def setUpClass(cls): """ A method called before each unit test in this class. """ # Give all species a low level Hf298 of 100 J/mol--this is not important for this test hf = (100.0, 'J/mol') lot = LevelOfTheory('test') cls.propene = ErrorCancelingSpecies(Molecule(smiles='CC=C'), hf, lot) cls.butane = ErrorCancelingSpecies(Molecule(smiles='CCCC'), hf, lot) cls.benzene = ErrorCancelingSpecies(Molecule(smiles='c1ccccc1'), hf, lot) cls.caffeine = ErrorCancelingSpecies( Molecule(smiles='CN1C=NC2=C1C(=O)N(C(=O)N2C)C'), hf, lot) cls.ethyne = ErrorCancelingSpecies(Molecule(smiles='C#C'), hf, lot)
def test_error_canceling_reactions(self): """ Test that ErrorCancelingReaction object can be created and that hf298 can be calculated for the target """ # Take ethane as the target lot = LevelOfTheory('test') ethane = ErrorCancelingSpecies(self.molecule1, (100.0, 'kJ/mol'), lot) methyl = ErrorCancelingSpecies(self.molecule2, (20.0, 'kcal/mol'), lot, (21000.0, 'cal/mol')) # This reaction is not an isodesmic reaction, but that does not matter for the unit test rxn = ErrorCancelingReaction(ethane, {methyl: 2}) self.assertAlmostEqual( rxn.calculate_target_thermo().value_si, 2 * 21000.0 * 4.184 - (2 * 20.0 * 4184 - 100.0 * 1000))
def test_error_canceling_species(self): """ Test that ErrorCancelingSpecies can be created properly """ lot = LevelOfTheory('test') error_canceling_species = ErrorCancelingSpecies( self.molecule1, (123.4, 'kcal/mol'), lot, (100.0, 'kJ/mol')) self.assertIsInstance(error_canceling_species, ErrorCancelingSpecies) self.assertAlmostEqual( error_canceling_species.low_level_hf298.value_si, 123.4 * 4184) # For target species the high level data is not given target_species = ErrorCancelingSpecies(self.molecule2, (10.1, 'J/mol'), lot) self.assertIs(target_species.high_level_hf298, None)
def test_specifying_absolute_file_paths(self): """Test specifying absolute file paths of statmech files""" h2o2_input = """#!/usr/bin/env python # -*- coding: utf-8 -*- bonds = {{'H-O': 2, 'O-O': 1}} externalSymmetry = 2 spinMultiplicity = 1 opticalIsomers = 1 energy = {{'b3lyp/6-311+g(3df,2p)': Log('{energy}')}} geometry = Log('{freq}') frequencies = Log('{freq}') rotors = [HinderedRotor(scanLog=Log('{scan}'), pivots=[1, 2], top=[1, 3], symmetry=1, fit='fourier')] """ abs_arkane_path = os.path.abspath(os.path.dirname( __file__)) # this is the absolute path to `.../RMG-Py/arkane` energy_path = os.path.join('arkane', 'data', 'H2O2', 'sp_a19032.out') freq_path = os.path.join('arkane', 'data', 'H2O2', 'freq_a19031.out') scan_path = os.path.join('arkane', 'data', 'H2O2', 'scan_a19034.out') h2o2_input = h2o2_input.format(energy=energy_path, freq=freq_path, scan=scan_path) h2o2_path = os.path.join(abs_arkane_path, 'data', 'H2O2', 'H2O2.py') if not os.path.exists(os.path.dirname(h2o2_path)): os.makedirs(os.path.dirname(h2o2_path)) with open(h2o2_path, 'w') as f: f.write(h2o2_input) h2o2 = Species(label='H2O2', smiles='OO') self.assertIsNone(h2o2.conformer) statmech_job = StatMechJob(species=h2o2, path=h2o2_path) statmech_job.level_of_theory = LevelOfTheory('b3lyp', '6-311+g(3df,2p)') statmech_job.load(pdep=False, plot=False) self.assertAlmostEqual(h2o2.conformer.E0.value_si, -146031.49933673252) os.remove(h2o2_path)
def setUpClass(cls): try: import pyomo as pyo except ImportError: pyo = None cls.pyo = pyo lot = LevelOfTheory('test') cls.propene = ErrorCancelingSpecies(Molecule(smiles='CC=C'), (100, 'kJ/mol'), lot, (105, 'kJ/mol')) cls.propane = ErrorCancelingSpecies(Molecule(smiles='CCC'), (75, 'kJ/mol'), lot, (80, 'kJ/mol')) cls.butane = ErrorCancelingSpecies(Molecule(smiles='CCCC'), (150, 'kJ/mol'), lot, (145, 'kJ/mol')) cls.butene = ErrorCancelingSpecies(Molecule(smiles='C=CCC'), (175, 'kJ/mol'), lot, (180, 'kJ/mol')) cls.pentane = ErrorCancelingSpecies(Molecule(smiles='CCCCC'), (200, 'kJ/mol'), lot, (190, 'kJ/mol')) cls.pentene = ErrorCancelingSpecies(Molecule(smiles='C=CCCC'), (225, 'kJ/mol'), lot, (220, 'kJ/mol')) cls.hexane = ErrorCancelingSpecies(Molecule(smiles='CCCCCC'), (250, 'kJ/mol'), lot, (260, 'kJ/mol')) cls.hexene = ErrorCancelingSpecies(Molecule(smiles='C=CCCCC'), (275, 'kJ/mol'), lot, (275, 'kJ/mol')) cls.benzene = ErrorCancelingSpecies(Molecule(smiles='c1ccccc1'), (-50, 'kJ/mol'), lot, (-80, 'kJ/mol')) cls.caffeine = ErrorCancelingSpecies( Molecule(smiles='CN1C=NC2=C1C(=O)N(C(=O)N2C)C'), (300, 'kJ/mol'), lot) cls.ethyne = ErrorCancelingSpecies(Molecule(smiles='C#C'), (200, 'kJ/mol'), lot)
def test_extract_level_of_theory(self): """ Test that a given level of theory can be extracted from the reference set database """ # Create a quick example database ref_data_1 = ReferenceDataEntry(ThermoData(H298=(100, 'kJ/mol', '+|-', 2))) ref_data_2 = ReferenceDataEntry(ThermoData(H298=(25, 'kcal/mol', '+|-', 1))) calc_data_1 = CalculatedDataEntry(ThermoData(H298=(110, 'kJ/mol'))) calc_data_2 = CalculatedDataEntry(ThermoData(H298=(120, 'kJ/mol'))) ethane = ReferenceSpecies(smiles='CC', reference_data={'precise': ref_data_1, 'less_precise': ref_data_2}, calculated_data={LevelOfTheory('good_chem'): calc_data_1, LevelOfTheory('bad_chem'): calc_data_2}, preferred_reference='less_precise') propane = ReferenceSpecies(smiles='CCC', reference_data={'precise': ref_data_1, 'less_precise': ref_data_2}, calculated_data={LevelOfTheory('good_chem'): calc_data_1, LevelOfTheory('bad_chem'): calc_data_2}) butane = ReferenceSpecies(smiles='CCCC', reference_data={'precise': ref_data_1, 'less_precise': ref_data_2}, calculated_data={LevelOfTheory('bad_chem'): calc_data_2}) database = ReferenceDatabase() database.reference_sets = {'testing_1': [ethane, butane], 'testing_2': [propane]} model_chem_list = database.extract_level_of_theory(LevelOfTheory('good_chem')) self.assertEqual(len(model_chem_list), 2) self.assertIsInstance(model_chem_list[0], ErrorCancelingSpecies) for spcs in model_chem_list: smiles = spcs.molecule.to_smiles() self.assertNotIn(smiles, ['CCCC']) self.assertIn(smiles, ['CC', 'CCC']) if smiles == 'CC': # Test that `less_precise` is the source since it was set manually as preferred self.assertAlmostEqual(spcs.high_level_hf298.value_si, 25.0*4184.0) if smiles == 'CCC': # Test that `precise` is the source since it has the lowest uncertainty self.assertAlmostEqual(spcs.high_level_hf298.value_si, 100.0*1000.0)
import pybel from rmgpy.molecule import Molecule as RMGMolecule import arkane.encorr.data as data from arkane.encorr.data import (Molecule, Stats, BACDatapoint, DatasetProperty, BACDataset, extract_dataset, geo_to_mol, _pybel_to_rmg) from arkane.encorr.reference import ReferenceDatabase from arkane.exceptions import BondAdditivityCorrectionError from arkane.modelchem import LOT, LevelOfTheory DATABASE = ReferenceDatabase() DATABASE.load() LEVEL_OF_THEORY = LevelOfTheory(method='wb97m-v', basis='def2-tzvpd', software='qchem') class TestDataLoading(unittest.TestCase): """ A class for testing that the quantum correction data is loaded correctly from the RMG database. """ def test_contains_data(self): """ Test that the necessary dictionaries are available. """ self.assertTrue(hasattr(data, 'atom_hf')) self.assertTrue(hasattr(data, 'atom_thermal')) self.assertTrue(hasattr(data, 'SOC'))
def to_arkane_level_of_theory( self, variant: Optional[str] = None, bac_type: str = 'p', comprehensive: bool = False, raise_error: bool = False, warn: bool = True, ) -> Optional[LevelOfTheory]: """ Convert ``Level`` to an Arkane ``LevelOfTheory`` instance. Args: variant (str, optional): Return a variant of the Arkane ``LevelOfTheory`` that matches an Arkane query. Allowed values are ``'freq'``, ``'AEC'``, ``'BEC'``. Returns ``None`` if no functioning variant was found. bac_type (str, optional): The BAC type ('p' or 'm') to use when searching for a ``LevelOfTheory`` variant for BAC. comprehensive (bool, optional): Whether to consider all relevant arguments if not looking for a variant. raise_error (bool, optional): Whether to raise an error if an AEC variant could not be found. warn (bool, optional): Whether to output a warning if an AEC variant could not be found. Returns: LevelOfTheory: The respective Arkane ``LevelOfTheory`` object """ if variant is None: if not comprehensive: # only add basis and software if needed kwargs = {'method': self.method} if self.basis is not None: kwargs['basis'] = self.basis kwargs['software'] = self.software return LevelOfTheory(**kwargs) else: # consider all relevant arguments kwargs = self.__dict__.copy() del kwargs['solvation_scheme_level'] del kwargs['method_type'] del kwargs['repr'] del kwargs['compatible_ess'] del kwargs['dispersion'] del kwargs['args'] if self.args is not None and self.args and all( [val for val in self.args.values()]): # only pass keyword arguments to Arkane (not blocks) if any([key == 'keyword' for key in self.args.keys()]): kwargs['args'] = list() for key1, val1 in self.args.items(): if key1 == 'keyword': for val2 in val1.values(): kwargs['args'].append(val2) break else: kwargs['args'] = None if self.dispersion is not None: if 'args' not in kwargs: kwargs['args'] = [self.dispersion] else: kwargs['args'].append(self.dispersion) if kwargs['method'] is not None: kwargs['method'].replace('f12a', 'f12').replace('f12b', 'f12') if kwargs['basis'] is not None: kwargs['basis'].replace('f12a', 'f12').replace('f12b', 'f12') return LevelOfTheory(**kwargs) else: # search for a functioning variant if variant not in ['freq', 'AEC', 'BAC']: raise ValueError( f'variant must be either "freq", "AEC", or "BAC", got "{variant}".' ) kwargs = {'method': self.method} if self.basis is not None: kwargs['basis'] = self.basis if standardize_name(self.method) in METHODS_THAT_REQUIRE_SOFTWARE: # add software if mandatory (otherwise, Arkane won't accept this object initialization) kwargs['software'] = self.software var_2 = LevelOfTheory(**kwargs) kwargs['software'] = self.software # add or overwrite software # start w/ the software argument (var_1) in case there are several entries that only vary by software var_1 = LevelOfTheory(**kwargs) if variant == 'freq': # if not found, the factor is set to exactly 1 if assign_frequency_scale_factor(level_of_theory=var_1) != 1: return var_1 if assign_frequency_scale_factor(level_of_theory=var_2) != 1: return var_2 return None if variant == 'AEC': try: arkane_data.atom_energies[var_1] return var_1 except KeyError: try: arkane_data.atom_energies[var_2] return var_2 except KeyError: if raise_error: raise ValueError( f'Missing Arkane atom energy corrections for {var_1}\n' f'(If you did not mean to compute thermo, set the compute_thermo ' f'argument to False to avoid this error.)') else: if warn: logger.warning( f'Missing Arkane atom energy corrections for {var_1}.' ) return None if variant == 'BAC': if bac_type not in ['p', 'm']: raise ValueError( f'bac_type must be either "p" or "m", got "{bac_type}".' ) bac = BAC(level_of_theory=var_1, bac_type=bac_type) if bac.bacs is None: bac = BAC(level_of_theory=var_2, bac_type=bac_type) if bac.bacs is None: logger.warning(f'Missing Arkane BAC for {var_2}.') return None else: return var_2 else: return var_1
############################################################################### """ This script contains unit tests for the :mod:`arkane.modelchem` module. """ import unittest from dataclasses import FrozenInstanceError from arkane.modelchem import (LOT, LevelOfTheory, CompositeLevelOfTheory, model_chem_to_lot, str_to_lot, get_software_id) # Instances for use in tests FREQ = LevelOfTheory( method='wB97X-D', basis='def2-TZVP', software='Gaussian 16', args='very-tight' ) ENERGY = LevelOfTheory( method='DLPNO-CCSD(T)-F12', basis='def2-TZVP', software='Orca' ) COMPOSITE = CompositeLevelOfTheory( freq=FREQ, energy=ENERGY ) # Representations corresponding to instances FREQ_REPR = "LevelOfTheory(method='wb97xd',basis='def2tzvp',software='gaussian',args=('verytight',))" ENERGY_REPR = "LevelOfTheory(method='dlpnoccsd(t)f12',basis='def2tzvp',software='orca')"
def setUpClass(cls): cls.lot_get = LevelOfTheory(method='CCSD(T)-F12', basis='cc-pVTZ-F12', software='Molpro') cls.lot_fit = LevelOfTheory(method='wB97M-V', basis='def2-TZVPD', software='Q-Chem') cls.lot_nonexisting = LevelOfTheory('notamethod') cls.bac = BAC(cls.lot_get) cls.tmp_melius_params = { 'atom_corr': { 'H': 1.0, 'C': 2.0, 'N': 3.0, 'O': 4.0, 'S': 5.0, 'F': 6.0, 'Cl': 7.0, 'Br': 8.0 }, 'bond_corr_length': { 'H': 1.0, 'C': 2.0, 'N': 3.0, 'O': 4.0, 'S': 5.0, 'F': 6.0, 'Cl': 7.0, 'Br': 8.0 }, 'bond_corr_neighbor': { 'H': 1.0, 'C': 2.0, 'N': 3.0, 'O': 4.0, 'S': 5.0, 'F': 6.0, 'Cl': 7.0, 'Br': 8.0 }, 'mol_corr': 1.0 } cls.tmp_petersson_params = { 'C-H': 1.0, 'C-C': 2.0, 'C=C': 3.0, 'C-O': 4.0 } # Set molecule, bonds, nums, and coords for testing Petersson and Melius BACs cls.multiplicity = 1 smi = 'C=C(OSC=S)C#CC1C(=O)N=CNC1SSC(O)C#N' mol = Molecule(smiles=smi, multiplicity=cls.multiplicity) cls.bonds = Counter( f'{b.atom1.element.symbol}{BOND_SYMBOLS[b.order]}{b.atom2.element.symbol}' for b in mol.get_all_edges()) pybel_mol = pybel.readstring('smi', smi) pybel_mol.addh() pybel_mol.make3D() mol_3d = _pybel_to_rmg(pybel_mol) cls.nums = [atom.number for atom in mol_3d.atoms] cls.coords = np.array([atom.coords for atom in mol_3d.atoms])