def test_get_bond_rearrangs(): if os.path.exists('test_bond_rearrangs.txt'): os.remove('test_bond_rearrangs.txt') # ethane --> Ch3 + Ch3 reac = Molecule(smiles='CC') prod = Molecule(atoms=[Atom('C', -8.3, 1.4, 0.0), Atom('C', 12, 1.7, -0.0), Atom('H', -8.6, 0.5, -0.5), Atom('H', -8.6, 2.3, -0.4), Atom('H', -8.6, 1.3, 1), Atom('H', 12.3, 1.7, -1.0), Atom('H', 12.4, 0.8, 0.4), Atom('H', 12.3, 2.5, 0.5)]) assert br.get_bond_rearrangs(ReactantComplex(reac), ProductComplex(prod), name='test') == [br.BondRearrangement(breaking_bonds=[(0, 1)])] # Rerunning the get function should read test_bond_rearrangs.txt, so modify it, swapping 0 and 1 in the breaking # bond then reopen with open('test_bond_rearrangs.txt', 'w') as rearr_file: print('fbond\n' 'bbonds\n' '1 0\n' 'end', file=rearr_file) rearr = br.get_bond_rearrangs(ReactantComplex(reac), ProductComplex(prod), name='test')[0] assert rearr == BondRearrangement(breaking_bonds=[(1, 0)]) assert br.get_bond_rearrangs(ReactantComplex(prod), ProductComplex(reac), name='test2') is None # If reactants and products are identical then the rearrangement is undetermined assert br.get_bond_rearrangs(ReactantComplex(reac), ProductComplex(reac), name='test3') is None os.remove('test_bond_rearrangs.txt')
def test_calc_delta_e(): r1 = reaction.Reactant(name='h', atoms=[Atom('H', 0.0, 0.0, 0.0)]) r1.energy = -0.5 r2 = reaction.Reactant(name='h', atoms=[Atom('H', 0.0, 0.0, 0.0)]) r2.energy = -0.5 tsguess = TSguess(atoms=None, reactant=ReactantComplex(r1), product=ProductComplex(r2)) tsguess.bond_rearrangement = BondRearrangement() ts = TransitionState(tsguess) ts.energy = -0.8 p = reaction.Product( name='hh', atoms=[Atom('H', 0.0, 0.0, 0.0), Atom('H', 1.0, 0.0, 0.0)]) p.energy = -1.0 reac = reaction.Reaction(r1, r2, p) reac.ts = ts assert -1E-6 < reac.calc_delta_e() < 1E-6 assert 0.2 - 1E-6 < reac.calc_delta_e_ddagger() < 0.2 + 1E-6
def test_sum_mep(): reactant = ReactantComplex(Molecule(smiles='C', charge=0, mult=1)) product = ProductComplex(Molecule(smiles='C', charge=0, mult=1)) product.graph.remove_edge(0, 1) product.graph.remove_edge(0, 2) # Fictitious PES pes = PES2d(reactant, product, r1s=np.linspace(1, 2, 3), r1_idxs=(0, 1), r2s=np.linspace(1, 2, 3), r2_idxs=(0, 2)) # Energies are all 0 apart from at the 'saddle point' (1, 1) for i in range(3): for j in range(3): pes.species[i, j] = deepcopy(reactant) pes.species[i, j].energy = 0 if i == j == 2: pes.species[i, j].graph = product.graph pes.species[1, 1].energy = 1 mep_sum = mep.get_sum_energy_mep(saddle_point_r1r2=(1.5, 1.5), pes_2d=pes) # Energy over the saddle point is 1 both sides -> 2 Ha assert mep_sum == 2
def test_constrained_opt(): os.chdir(os.path.join(here, 'data')) mol = Molecule(name='h3', mult=2, charge=0, atoms=[ Atom('H', 0.0, 0.0, 0.0), Atom('H', 0.7, 0.0, 0.0), Atom('H', 1.7, 0.0, 0.0) ]) Config.XTB.path = here # A path that exists ts_guess = get_ts_guess_constrained_opt( reactant=ReactantComplex(mol), distance_consts={(0, 1): 1.0}, method=orca, keywords=Config.ORCA.keywords.low_opt, name='template_ts_guess', product=ProductComplex(mol)) assert ts_guess.n_atoms == 3 os.remove('xcontrol_template_ts_guess_constrained_opt_ll_xtb') os.remove('template_ts_guess_constrained_opt_ll_xtb.xyz') os.remove('template_ts_guess_constrained_opt_orca.inp') os.chdir(here)
def test_reaction_warnings(): test_reac = Reactant(name='test', smiles='C') test_reac.energy = -1 test_prod = Product(name='test', smiles='C') test_prod.energy = -1.03187251 tsguess = TSguess(atoms=test_reac.atoms, reactant=ReactantComplex(test_reac), product=ProductComplex()) tsguess.bond_rearrangement = BondRearrangement() ts = TransitionState(tsguess) ts.energy = -0.98 ts.imaginary_frequencies = [-100] reaction = Reaction(test_reac, test_prod) reaction.ts = None # Should be some warning with no TS assert len( plotting.get_reaction_profile_warnings(reactions=[reaction])) > 10 # Should be no warnings with a TS that exists and has an energy and one # imaginary freq reaction.ts = ts warnings = plotting.get_reaction_profile_warnings(reactions=[reaction]) assert 'None' in warnings
def test_plot_reaction_profile(): r = Reactant(name='reactant', smiles='C') p = Product(name='product', smiles='C') tsguess = TSguess(atoms=r.atoms, reactant=ReactantComplex(r), product=ProductComplex(p)) tsguess.bond_rearrangement = BondRearrangement() ts = TransitionState(tsguess) reaction = Reaction(r, p) reaction.ts = ts plotting.plot_reaction_profile(reactions=[reaction], units=KjMol, name='test') assert os.path.exists('test_reaction_profile.png') os.remove('test_reaction_profile.png') with pytest.raises(AssertionError): plotting.plot_reaction_profile(reactions=[reaction], units=KjMol, name='test', free_energy=True, enthalpy=True) return None
def test_3b(): reac = Molecule(atoms=[Atom('H', 0, 0, 0), Atom('H', 0.6, 0, 0), Atom('H', 1.2, 0, 0), Atom('H', 1.8, 0, 0)]) make_graph(reac, allow_invalid_valancies=True) prod = Molecule(atoms=[Atom('H', 0, 0, 0), Atom('H', 10, 0, 0), Atom('H', 20, 0, 0), Atom('H', 30, 0, 0)]) # Reactants to products must break three bonds but this is not yet supported in any form assert br.get_bond_rearrangs(ReactantComplex(reac), ProductComplex(prod), name='3b_test') is None
def test_ts_conformer(tmpdir): os.chdir(tmpdir) ch3cl = Reactant(charge=0, mult=1, atoms=[ Atom('Cl', 1.63664, 0.02010, -0.05829), Atom('C', -0.14524, -0.00136, 0.00498), Atom('H', -0.52169, -0.54637, -0.86809), Atom('H', -0.45804, -0.50420, 0.92747), Atom('H', -0.51166, 1.03181, -0.00597) ]) f = Reactant(charge=-1, mult=1, atoms=[Atom('F', 4.0, 0.0, 0.0)]) ch3f = Product(charge=0, mult=1, atoms=[ Atom('C', -0.05250, 0.00047, -0.00636), Atom('F', 1.31229, -0.01702, 0.16350), Atom('H', -0.54993, -0.04452, 0.97526), Atom('H', -0.34815, 0.92748, -0.52199), Atom('H', -0.36172, -0.86651, -0.61030) ]) cl = Reactant(charge=-1, mult=1, atoms=[Atom('Cl', 4.0, 0.0, 0.0)]) f_ch3cl_tsguess = TSguess(reactant=ReactantComplex(f, ch3cl), product=ProductComplex(ch3f, cl), atoms=[ Atom('F', -2.66092, -0.01426, 0.09700), Atom('Cl', 1.46795, 0.05788, -0.06166), Atom('C', -0.66317, -0.01826, 0.02488), Atom('H', -0.78315, -0.58679, -0.88975), Atom('H', -0.70611, -0.54149, 0.97313), Atom('H', -0.80305, 1.05409, 0.00503) ]) f_ch3cl_tsguess.bond_rearrangement = BondRearrangement(breaking_bonds=[ (2, 1) ], forming_bonds=[(0, 2)]) f_ch3cl_ts = TransitionState(ts_guess=f_ch3cl_tsguess) atoms = conf_gen.get_simanl_atoms( species=f_ch3cl_ts, dist_consts=get_distance_constraints(f_ch3cl_ts)) regen = Molecule(name='regenerated_ts', charge=-1, mult=1, atoms=atoms) # regen.print_xyz_file() # Ensure the making/breaking bonds retain their length regen_coords = regen.get_coordinates() assert are_coords_reasonable(regen_coords) is True assert 1.9 < np.linalg.norm(regen_coords[0] - regen_coords[2]) < 2.1 assert 2.0 < np.linalg.norm(regen_coords[1] - regen_coords[2]) < 2.2 os.chdir(here)
def test_2b(): reac = Molecule(atoms=[Atom('H', 0, 0, 0), Atom('H', 0.6, 0, 0), Atom('H', 1.2, 0, 0)]) make_graph(reac, allow_invalid_valancies=True) prod = Molecule(atoms=[Atom('H', 0, 0, 0), Atom('H', 10, 0, 0), Atom('H', 20, 0, 0)]) # Reactants to products must break two bonds assert len(br.get_bond_rearrangs(ReactantComplex(reac), ProductComplex(prod), name='2b_test')) == 1 os.remove('2b_test_bond_rearrangs.txt') assert br.get_fbonds_bbonds_2b(reac, prod, [], [[(0, 1), (1, 2)]], [], [], [(0, 2)], []) == [br.BondRearrangement(breaking_bonds=[(0, 1), (1, 2)])]
def test_two_possibles(): ch2ch3f = Molecule(name='radical', charge=0, mult=2, smiles='FC[C]([H])[H]') ch3ch2f = Molecule(name='radical', charge=0, mult=2, smiles='C[C]([H])F') rearrs = br.get_bond_rearrangs(ReactantComplex(ch2ch3f), ProductComplex(ch3ch2f), name='H_migration') # There are two possibilities for H migration by they should be considered the same assert len(rearrs) == 1 os.remove('H_migration_bond_rearrangs.txt')
def test_multiple_possibilities(): r1 = Molecule(name='h_dot', smiles='[H]') r2 = Molecule(name='methane', smiles='C') p1 = Molecule(name='h2', smiles='[HH]') p2 = Molecule(name='ch3_dot', smiles='[CH3]') reac = ReactantComplex(r1, r2) reac.print_xyz_file() rearrs = br.get_bond_rearrangs(reac, ProductComplex(p1, p2), name='H_subst') os.remove('H_subst_bond_rearrangs.txt') # All H abstractions are the same assert len(rearrs) == 1
def test_plot_reaction_profile(): r = Reactant(name='reactant', smiles='C') p = Product(name='product', smiles='C') tsguess = TSguess(atoms=r.atoms, reactant=ReactantComplex(r), product=ProductComplex(p)) tsguess.bond_rearrangement = BondRearrangement() ts = TransitionState(tsguess) reaction = Reaction(r, p) reaction.ts = ts plotting.plot_reaction_profile(reactions=[reaction], units=KjMol, name='test_reaction') assert os.path.exists('test_reaction_reaction_profile.png') os.remove('test_reaction_reaction_profile.png')
def test_plot_reaction_profile(): # only tests the file is created with the right name os.chdir(os.path.join(here, 'data')) r = Reactant(name='reactant', smiles='C') p = Product(name='product', smiles='C') tsguess = TSguess(atoms=r.atoms, reactant=ReactantComplex(r), product=ProductComplex(p)) tsguess.bond_rearrangement = BondRearrangement() ts = TransitionState(tsguess) reaction = Reaction(r, p) reaction.ts = ts plotting.plot_reaction_profile(reactions=[reaction], units=KjMol, name='test_reaction') assert os.path.exists('test_reaction_reaction_profile.png') os.remove('test_reaction_reaction_profile.png') os.chdir(here)
def test_constrained_opt(): mol = Molecule(name='h3', mult=2, charge=0, atoms=[ Atom('H', 0.0, 0.0, 0.0), Atom('H', 0.7, 0.0, 0.0), Atom('H', 1.7, 0.0, 0.0) ]) # Spoof an XTB install Config.XTB.path = here ts_guess = get_ts_guess_constrained_opt( reactant=ReactantComplex(mol), distance_consts={(0, 1): 1.0}, method=XTB(), keywords=Config.XTB.keywords.low_opt, name='template_ts_guess', product=ProductComplex(mol)) assert ts_guess.n_atoms == 3
def test_fb_rp_isomorphic(): reac = ReactantComplex(f, ch3cl) prod = ProductComplex(ch3f, cl) assert f_b_isomorphic_to_r_p(forwards=prod, backwards=reac, reactant=reac, product=prod) assert f_b_isomorphic_to_r_p(forwards=reac, backwards=prod, reactant=reac, product=prod) assert not f_b_isomorphic_to_r_p( forwards=reac, backwards=reac, reactant=reac, product=prod) # Check for e.g. failed optimisation of the forwards displaced complex mol_no_atoms = Product() assert not f_b_isomorphic_to_r_p( forwards=mol_no_atoms, backwards=reac, reactant=reac, product=prod)
def test_calculate_reaction_profile_energies(): test_reac = Reactant(name='test', smiles='C') test_reac.energy = -1 test_prod = Product(name='test', smiles='C') test_prod.energy = -1.03187251 tsguess = TSguess(atoms=test_reac.atoms, reactant=ReactantComplex(test_reac), product=ProductComplex()) tsguess.bond_rearrangement = BondRearrangement() ts = TransitionState(tsguess) ts.energy = -0.96812749 reaction = Reaction(test_reac, test_prod) reaction.ts = ts energies = plotting.calculate_reaction_profile_energies( reactions=[reaction], units=KcalMol) # Energies have been set to ∆E = -20 and ∆E‡ = 20 kcal mol-1 respectively assert energies[0].item() == 0 assert 19 < energies[1].item() < 21 assert -21 < energies[2].item() < -19 # Copying the reaction should give relative energies [0, 20, -20, 0, -40] energies = plotting.calculate_reaction_profile_energies( reactions=[reaction, deepcopy(reaction)], units=KcalMol) # Energies have been set to ∆E = -20 and ∆E‡ = 20 kcal mol-1 respectively assert energies[0].item() == 0 assert -0.1 < energies[3].item() < 0.1 assert -41 < energies[4].item() < -39
Atom('H', -0.51166, 1.03181, -0.00597) ]) f = Reactant(charge=-1, mult=1, atoms=[Atom('F', 4.0, 0.0, 0.0)]) reac_complex = ReactantComplex(f, ch3cl) ch3f = Product(charge=0, mult=1, atoms=[ Atom('C', -0.05250, 0.00047, -0.00636), Atom('F', 1.31229, -0.01702, 0.16350), Atom('H', -0.54993, -0.04452, 0.97526), Atom('H', -0.34815, 0.92748, -0.52199), Atom('H', -0.36172, -0.86651, -0.61030) ]) cl = Product(charge=-1, mult=1, atoms=[Atom('Cl', 4.0, 0.0, 0.0)]) product_complex = ProductComplex(ch3f, cl) @testutils.work_in_zipped_dir(os.path.join(here, 'data', 'ts_guess.zip')) def test_ts_template_save(): ts_graph = reac_complex.graph.copy() # Add the F-C bond as active ts_graph.add_edge(0, 2, active=True) # Remove then re-add the C-Cl bond as active ts_graph.remove_edge(1, 2) ts_graph.add_edge(1, 2, active=True) truncated_graph = get_truncated_active_mol_graph(ts_graph)
from autode.pes.pes import get_closest_species from autode.species.complex import ReactantComplex, ProductComplex from autode.atoms import Atom from autode.species.species import Species from autode.mol_graphs import make_graph from autode.exceptions import NoClosestSpecies from autode.pes.pes import FormingBond, BreakingBond from autode.reactions.reaction import Reaction from autode.species.molecule import Reactant, Product import pytest import numpy as np mol = Species(name='H2', charge=0, mult=1, atoms=[Atom('H'), Atom('H', z=1.0)]) make_graph(mol) reactant = ReactantComplex(mol, mol) product = ProductComplex(mol, mol) pes = PES2d(reactant=reactant, product=reactant, r1s=np.linspace(1, 3, 3), r1_idxs=(0, 1), r2s=np.linspace(1, 0, 2), r2_idxs=(2, 3)) def test_2d_pes_class(): assert reactant.n_atoms == 4 assert pes.rs.shape == (3, 2) assert pes.species.shape == (3, 2)
def test_get_ts_guess_2dscan(): ch3cl_f = Reactant(name='CH3Cl_F-', charge=-1, mult=1, atoms=[ Atom('F', -4.14292, -0.24015, 0.07872), Atom('Cl', 1.63463, 0.09787, -0.02490), Atom('C', -0.14523, -0.00817, 0.00208), Atom('H', -0.47498, -0.59594, -0.86199), Atom('H', -0.45432, -0.49900, 0.93234), Atom('H', -0.56010, 1.00533, -0.04754) ]) ch3f_cl = Product(name='CH3Cl_F-', charge=-1, mult=1, atoms=[ Atom('F', 1.63463, 0.09787, -0.02490), Atom('Cl', -4.14292, -0.24015, 0.07872), Atom('C', -0.14523, -0.00817, 0.00208), Atom('H', -0.47498, -0.59594, -0.86199), Atom('H', -0.45432, -0.49900, 0.93234), Atom('H', -0.56010, 1.00533, -0.04754) ]) # H H # F- C--Cl -> F--C Cl- # H H H H pes = pes_2d.PES2d(reactant=ReactantComplex(ch3cl_f), product=ProductComplex(ch3f_cl), r1s=np.linspace(4.0, 1.5, 9), r1_idxs=(0, 2), r2s=np.linspace(1.78, 4.0, 8), r2_idxs=(1, 2)) pes.calculate(name='SN2_PES', method=xtb, keywords=xtb.keywords.low_opt) assert pes.species[0, 1] is not None assert -13.13 < pes.species[0, 1].energy < -13.11 assert pes.species.shape == (9, 8) assert pes.rs.shape == (9, 8) assert type(pes.rs[0, 1]) == tuple assert pes.rs[1, 1] == (np.linspace(4.0, 1.5, 9)[1], np.linspace(1.78, 4.0, 8)[1]) # Fitting the surface with a 2D polynomial up to order 3 in r1 and r2 i.e. # r1^3r2^3 pes.fit(polynomial_order=3) assert pes.coeff_mat is not None assert pes.coeff_mat.shape == (4, 4) # Includes r1^0 etc. pes.print_plot(name='pes_plot') assert os.path.exists('pes_plot.png') os.remove('pes_plot.png') # Products should be made on this surface assert pes.products_made() # Get the TS guess from this surface calling all the above functions reactant = ReactantComplex(ch3cl_f) fbond = FormingBond(atom_indexes=(0, 2), species=reactant) fbond.final_dist = 1.5 bbond = BreakingBond(atom_indexes=(1, 2), species=reactant, reaction=Reaction(ch3cl_f, ch3f_cl)) bbond.final_dist = 4.0 ts_guess = pes_2d.get_ts_guess_2d(reactant=reactant, product=ProductComplex(ch3f_cl), bond1=fbond, bond2=bbond, polynomial_order=3, name='SN2_PES', method=xtb, keywords=xtb.keywords.low_opt, dr=0.3) assert ts_guess is not None assert ts_guess.n_atoms == 6 assert ts_guess.energy is None assert 1.9 < ts_guess.get_distance(0, 2) < 2.1 assert 1.9 < ts_guess.get_distance(1, 2) < 2.0
# Override the availability of orca.available = True xtb.available = True # Set the reactant and product complexes h1 = Atom(atomic_symbol='H', x=0.0, y=0.0, z=0.0) h2 = Atom(atomic_symbol='H', x=0.0, y=0.0, z=0.7) h3 = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.7) hydrogen = Molecule(name='H2', atoms=[h1, h2], charge=0, mult=1) h = Molecule(name='H', atoms=[h3], charge=0, mult=2) reac = ReactantComplex(hydrogen, h) prod = ProductComplex(h, hydrogen) def test_get_ts_guess_1dscan(): os.chdir(os.path.join(here, 'data')) fbond = FormingBond(atom_indexes=(1, 2), species=reac) fbond.final_dist = 0.7 ts_guess = get_ts_guess_1d(name='H+H2_H2+H', reactant=reac, product=prod, bond=fbond, method=orca, keywords=opt_keywords, dr=0.06)