def test_set_pi_bonds(): ethene = Species(name='ethene', charge=0, mult=1, atoms=[ Atom('C', -2.20421, 0.40461, 0.00000), Atom('C', -0.87115, 0.38845, 0.00000), Atom('H', -2.76098, -0.22576, 0.68554), Atom('H', -2.74554, 1.04829, -0.68554), Atom('H', -0.32982, -0.25523, 0.68554), Atom('H', -0.31437, 1.01882, -0.68554) ]) mol_graphs.make_graph(ethene) assert ethene.graph.edges[0, 1]['pi'] is True assert ethene.graph.edges[1, 0]['pi'] is True assert ethene.graph.edges[0, 2]['pi'] is False acetylene = Species(name='acetylene', charge=0, mult=1, atoms=[ Atom('C', -2.14031, 0.40384, 0.00000), Atom('C', -0.93505, 0.38923, 0.00000), Atom('H', -3.19861, 0.41666, 0.00000), Atom('H', 0.12326, 0.37640, 0.00000) ]) mol_graphs.make_graph(acetylene) assert acetylene.graph.edges[0, 1]['pi'] is True assert acetylene.graph.edges[0, 2]['pi'] is False
def test_species_isomorphism(): h2.graph = None h2_copy = Species(name='H2', atoms=[h_a, h_b], charge=0, mult=1) h2_copy.graph = None with pytest.raises(NoMolecularGraph): assert mol_graphs.species_are_isomorphic(h2, h2_copy) # With molecular graphs the species should be isomorphic mol_graphs.make_graph(h2) mol_graphs.make_graph(h2_copy) assert mol_graphs.species_are_isomorphic(h2, h2_copy) # Shift one of the atoms far away and remake the graph h2_copy.atoms[1].translate(vec=np.array([10, 0, 0])) mol_graphs.make_graph(h2_copy) assert mol_graphs.species_are_isomorphic(h2, h2_copy) is False # Generating a pair of conformers that are isomporhpic should return that # the species are again isomorphic h2.conformers = [ Conformer(name='h2_conf', atoms=[h_a, h_b], charge=0, mult=1) ] h2_copy.conformers = [ Conformer(name='h2_conf', atoms=[h_a, h_b], charge=0, mult=1) ] assert mol_graphs.species_are_isomorphic(h2, h2_copy)
def test_species_class(): assert hasattr(mol, 'print_xyz_file') assert hasattr(mol, 'translate') assert hasattr(mol, 'rotate') assert hasattr(mol, 'get_coordinates') assert hasattr(mol, 'set_atoms') assert hasattr(mol, 'set_coordinates') assert mol.charge == 0 assert mol.mult == 1 assert mol.name == 'H2' assert not mol.is_explicitly_solvated() # A not very sensible water geometry! water = Species(name='H2O', charge=0, mult=1, atoms=[Atom('O'), Atom('H', z=-1), Atom('H', z=1)]) # Base class for molecules and TSs and complexes shouldn't have a # implemented conformer method – needs to do different things based on the # type of species to find conformers with pytest.raises(NotImplementedError): water.find_lowest_energy_conformer(lmethod=xtb)
def test_mapping(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.4) h3_a = Species(name='template', charge=0, mult=1, atoms=[h_a, h_b, h_c]) mol_graphs.make_graph(species=h3_a, allow_invalid_valancies=True) h3_b = Species(name='template', charge=0, mult=1, atoms=[h_a, h_b, h_c]) mol_graphs.make_graph(species=h3_b, allow_invalid_valancies=True) # Isomorphic (identical) graphs should have at least one mapping between them mapping = mol_graphs.get_mapping(h3_b.graph, h3_a.graph) assert mapping is not None assert type(mapping) == dict
def test_subgraph_isomorphism(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.4) h_d = Atom(atomic_symbol='H', x=0.0, y=0.0, z=2.1) h4 = Species(name='H4', atoms=[h_a, h_b, h_c, h_d], charge=0, mult=1) mol_graphs.make_graph(h4) assert mol_graphs.is_subgraph_isomorphic(larger_graph=h4.graph, smaller_graph=h2.graph) is True # H3 in a triangular arrangement should not be sub-graph isomorphic to linear H4 h_e = Atom(atomic_symbol='H', x=0.3, y=0.0, z=0.3) h3 = Species(name='H_H', charge=0, mult=1, atoms=[h_a, h_b, h_e]) mol_graphs.make_graph(h3, allow_invalid_valancies=True) assert mol_graphs.is_subgraph_isomorphic(larger_graph=h4.graph, smaller_graph=h3.graph) is False
def test_species_copy(): species = Species(name='h', charge=0, mult=2, atoms=[Atom('H')]) species_copy = species.copy() species_copy.charge = 1 assert species.charge != species_copy.charge species_copy.mult = 3 assert species.mult != species_copy.mult atom = species_copy.atoms[0] atom.translate(vec=np.array([1.0, 1.0, 1.0])) assert np.linalg.norm(species.atoms[0].coord - atom.coord) > 1
def test_not_isomorphic(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.0) h2_b = Species(name='template', charge=0, mult=1, atoms=[h_a, h_c]) mol_graphs.make_graph(species=h2_b, rel_tolerance=0.3) assert mol_graphs.is_isomorphic(h2.graph, h2_b.graph) is False
def test_not_isomorphic2(): c = Atom(atomic_symbol='C', x=0.0, y=0.0, z=0.7) ch = Species(name='ch', atoms=[h_a, c], charge=0, mult=2) mol_graphs.make_graph(ch) assert mol_graphs.is_isomorphic(h2.graph, ch.graph) is False
def test_correct_imag_mode(): os.chdir(os.path.join(here, 'data')) bond_rearrangement = BondRearrangement(breaking_bonds=[(4, 1), (4, 18)], forming_bonds=[(1, 18)]) g09 = G09() g09.available = True calc = Calculation(name='tmp', molecule=ReactantComplex( Reactant(smiles='CC(C)(C)C1C=CC=C1')), method=g09, keywords=Config.G09.keywords.opt_ts) calc.output.filename = 'correct_ts_mode_g09.log' calc.output.set_lines() f_displaced_atoms = get_displaced_atoms_along_mode(calc, mode_number=6, disp_magnitude=1.0) f_species = Species(name='f_displaced', atoms=f_displaced_atoms, charge=0, mult=1) # Charge & mult are placeholders b_displaced_atoms = get_displaced_atoms_along_mode(calc, mode_number=6, disp_magnitude=-1.0) b_species = Species(name='b_displaced', atoms=b_displaced_atoms, charge=0, mult=1) # With the correct mode no other bonds are made assert not imag_mode_generates_other_bonds( ts=calc.molecule, f_species=f_species, b_species=b_species, bond_rearrangement=bond_rearrangement) calc.output.filename = 'incorrect_ts_mode_g09.log' calc.output.set_lines() assert not imag_mode_has_correct_displacement(calc, bond_rearrangement) os.chdir(here)
def test_edge_cases(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.6) # For H3 with a slightly longer bond on one side there should only be 1 'bond' h3 = Species(name='H2', atoms=[h_a, h_b, h_c], charge=0, mult=1) mol_graphs.make_graph(h3) assert h3.graph.number_of_edges() == 1 assert h3.graph.number_of_nodes() == 3
def test_ts_template(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.4) ts_template = Species(name='template', charge=0, mult=1, atoms=[h_a, h_b, h_c]) mol_graphs.make_graph(species=ts_template, allow_invalid_valancies=True) ts_template.graph.edges[0, 1]['active'] = True ts = Species(name='template', charge=0, mult=1, atoms=[h_a, h_b, h_c]) mol_graphs.make_graph(species=ts, allow_invalid_valancies=True) ts.graph.edges[1, 2]['active'] = True mapping = mol_graphs.get_mapping_ts_template(ts.graph, ts_template.graph) assert mapping is not None assert type(mapping) == dict assert mol_graphs.is_isomorphic(ts.graph, ts_template.graph, ignore_active_bonds=True)
def test_species_solvent(): assert mol.solvent is None solvated_mol = Species(name='H2', atoms=[h1, h2], charge=0, mult=1, solvent_name='water') assert type(solvated_mol.solvent) == Solvent
def test_truncated_active_graph(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.4) h_d = Atom(atomic_symbol='H', x=0.0, y=0.0, z=2.1) ts = Species(name='template', charge=0, mult=1, atoms=[h_a, h_b, h_c, h_d]) mol_graphs.make_graph(species=ts, allow_invalid_valancies=True) # H--active--H--H--H should truncate by keeping only the nearest neighbours to the first two atoms truncated_graph = mol_graphs.get_truncated_active_mol_graph(ts.graph, active_bonds=[(0, 1)]) assert truncated_graph.number_of_nodes() == 3 assert truncated_graph.number_of_edges() == 2
def test_remove_bonds(): b3h6 = Species(name='diborane', charge=0, mult=1, atoms=[Atom('B', -1.97106, 0.36170, -0.23984), Atom('H', -0.91975, -0.06081, 0.43901), Atom('H', -2.14001, -0.24547, -1.26544), Atom('H', -2.99029, 0.31275, 0.39878), Atom('B', -0.49819, 1.17500, 0.23984), Atom('H', 0.52102, 1.22392, -0.39880), Atom('H', -0.32919, 1.78217, 1.26543), Atom('H', -1.54951, 1.59751, -0.43898)]) mol_graphs.make_graph(species=b3h6) assert b3h6.graph.number_of_edges() == 6 assert b3h6.graph.number_of_nodes() == 8 # Boron atoms should be 3 fold valent assert len(list(b3h6.graph.neighbors(0))) == 3 assert len(list(b3h6.graph.neighbors(4))) == 3
def imag_mode_has_correct_displacement(calc, bond_rearrangement, disp_mag=1.0, delta_threshold=0.3, req_all=True): """ Check whether the imaginary mode in a calculation with a hessian forms and breaks the correct bonds Arguments: calc (autode.calculation.Calculation): bond_rearrangement (autode.bond_rearrangement.BondRearrangement): Keyword Arguments: disp_mag (float): delta_threshold (float): Required ∆r on a bond for the bond to be considered as forming req_all (bool): Require all the bonds to have the correct displacements Returns: (bool): """ logger.info('Checking displacement on imaginary mode forms the correct' ' bonds') ts_species = deepcopy(calc.molecule) f_displaced_atoms = get_displaced_atoms_along_mode(calc, mode_number=6, disp_magnitude=disp_mag) f_species = Species(name='f_displaced', atoms=f_displaced_atoms, charge=0, mult=1) # Charge & mult are placeholders b_displaced_atoms = get_displaced_atoms_along_mode( calc, mode_number=6, disp_magnitude=-disp_mag) b_species = Species(name='b_displaced', atoms=b_displaced_atoms, charge=0, mult=1) if imag_mode_generates_other_bonds(ts_species, f_species, b_species, bond_rearrangement): logger.warning('Imaginary mode generates bonds that are not active..') return False # Product could be either the forward displaced molecule or the backwards # equivalent for product in (f_species, b_species): fbond_bbond_correct_disps = [] for fbond in bond_rearrangement.fbonds: ts_dist = ts_species.get_distance(*fbond) p_dist = product.get_distance(*fbond) # Displaced distance towards products should be shorter than the # distance at the TS if the bond is forming if ts_dist - p_dist > delta_threshold: fbond_bbond_correct_disps.append(True) else: fbond_bbond_correct_disps.append(False) for bbond in bond_rearrangement.bbonds: ts_dist = ts_species.get_distance(*bbond) p_dist = product.get_distance(*bbond) # Displaced distance towards products should be longer than the # distance at the TS if the bond is breaking if p_dist - ts_dist > delta_threshold: fbond_bbond_correct_disps.append(True) else: fbond_bbond_correct_disps.append(False) logger.info(f'List of forming and breaking bonds that have the ' f'correct properties {fbond_bbond_correct_disps}') if all(fbond_bbond_correct_disps) and req_all: logger.info(f'{product.name} afforded the correct bond ' f'forming/breaking reactants -> products') return True if not req_all and any(fbond_bbond_correct_disps): logger.info('At least one bond had the correct displacement') return True logger.warning('Displacement along the imaginary mode did not form and ' 'break the correct bonds') return False
from autode.wrappers.ORCA import orca from autode.wrappers.XTB import xtb from autode.atoms import Atom from autode.solvent.solvents import Solvent from autode.exceptions import NoAtomsInMolecule from copy import deepcopy import numpy as np import pytest import os here = os.path.dirname(os.path.abspath(__file__)) h1 = Atom('H') h2 = Atom('H', z=1.0) mol = Species(name='H2', atoms=[h1, h2], charge=0, mult=1) def test_species_class(): assert hasattr(mol, 'print_xyz_file') assert hasattr(mol, 'translate') assert hasattr(mol, 'rotate') assert hasattr(mol, 'get_coordinates') assert hasattr(mol, 'set_atoms') assert hasattr(mol, 'set_coordinates') assert mol.charge == 0 assert mol.mult == 1 assert mol.name == 'H2'
def test_isomorphic_graphs(): h2_alt = Species(name='H2', atoms=[h_b, h_a], charge=0, mult=1) mol_graphs.make_graph(h2_alt) assert mol_graphs.is_isomorphic(h2.graph, h2_alt.graph) is True
def test_set_lowest_energy_conformer(): from autode.mol_graphs import make_graph hb = Atom('H', z=0.7) hydrogen = Species(name='H2', atoms=[h1, hb], charge=0, mult=1) make_graph(hydrogen) hydrogen_wo_e = Species(name='H2', atoms=[h1, hb], charge=0, mult=1) hydrogen_with_e = Species(name='H2', atoms=[h1, hb], charge=0, mult=1) hydrogen_with_e.energy = -1 hydrogen.conformers = [hydrogen_wo_e, hydrogen_with_e] hydrogen._set_lowest_energy_conformer() # Conformers without energy should be skipped assert hydrogen.energy == -1 # Conformers with a different molecular graph should be skipped h_atom = Species(name='H', atoms=[Atom('H')], charge=0, mult=1) h_atom.energy = -2 hydrogen.conformers = [hydrogen_with_e, h_atom] assert hydrogen.energy == -1
from autode.pes.pes_2d import PES2d 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(atomic_symbol='H', x=0.0, y=0.0, z=0.0), Atom(atomic_symbol='H', x=0.0, y=0.0, 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)
from autode.species.molecule import Molecule from autode.atoms import Atom from autode.conformers import Conformer from autode.input_output import xyz_file_to_atoms from . import testutils import networkx as nx import numpy as np import pytest import os here = os.path.dirname(os.path.abspath(__file__)) h_a = Atom(atomic_symbol='H', x=0.0, y=0.0, z=0.0) h_b = Atom(atomic_symbol='H', x=0.0, y=0.0, z=0.7) h2 = Species(name='H2', atoms=[h_a, h_b], charge=0, mult=1) mol_graphs.make_graph(h2) g = nx.Graph() edges = [(0, 1), (1, 2), (2, 0), (0, 3), (3, 4)] for edge in edges: g.add_edge(*edge) def test_graph_generation(): assert h2.graph.number_of_edges() == 1 assert h2.graph.number_of_nodes() == 2 assert h2.graph.nodes[0]['atom_label'] == 'H'
from autode.species.complex import ReactantComplex from autode.species.species import Species from autode.atoms import Atom from autode.substitution import SubstitutionCentre from autode.mol_graphs import make_graph from autode.substitution import attack_cost from autode.substitution import get_cost_rotate_translate import numpy as np nh3 = Species(name='nh3', charge=0, mult=1, atoms=[Atom('N', -3.13130, 0.40668, -0.24910), Atom('H', -3.53678, 0.88567, 0.55690), Atom('H', -3.33721, 1.03222, -1.03052), Atom('H', -3.75574, -0.38765, -0.40209)]) make_graph(nh3) ch3cl = Species(name='CH3Cl', charge=0, mult=1, atoms=[Atom('Cl', 1.63751, -0.03204, -0.01858), Atom('C', -0.14528, 0.00318, 0.00160), Atom('H', -0.49672, -0.50478, 0.90708), Atom('H', -0.51741, -0.51407, -0.89014), Atom('H', -0.47810, 1.04781, 0.00015)]) make_graph(ch3cl) def test_attack(): reactant = ReactantComplex(nh3, ch3cl) subst_centre = SubstitutionCentre(a_atom_idx=0, c_atom_idx=5, x_atom_idx=4, a_atom_nn_idxs=[1, 2, 3]) subst_centre.r0_ac = 1.38
from autode.pes.pes_2d import PES2d 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)
def test_is_linear(): h_atom = Species(name='h', atoms=[Atom('H')], charge=0, mult=1) assert not h_atom.is_linear() dihydrogen = Species(name='h2', atoms=[Atom('H'), Atom('H', x=1)], charge=0, mult=1) assert dihydrogen.is_linear() water = Species(name='water', charge=0, mult=1, atoms=[ Atom('O', x=-1.52, y=2.72), Atom('H', x=-0.54, y=2.72), Atom('H', x=-1.82, y=2.82, z=-0.92) ]) assert not water.is_linear() lin_water = Species(name='linear_water', charge=0, mult=1, atoms=[ Atom('O', x=-1.52, y=2.72), Atom('H', x=-1.21, y=2.51, z=1.03), Atom('H', x=-1.82, y=2.82, z=-0.92) ]) assert lin_water.is_linear() close_lin_water = Species(name='linear_water', charge=0, mult=1, atoms=[ Atom('O', x=-1.52, y=2.72), Atom('H', x=-0.90, y=2.36, z=0.89), Atom('H', x=-1.82, y=2.82, z=-0.92) ]) assert not close_lin_water.is_linear() acetylene = Molecule(smiles='C#C') assert acetylene.is_linear()