Beispiel #1
0
    def calculate_reaction_profile(self, units=KcalMol, with_complexes=False):
        """
        Calculate and plot a reaction profile for this reaction in some units

        Arguments:
            units (autode.units.Unit):
            with_complexes (bool): Calculate the lowest energy conformers
                                   of the reactant and product complexes
        """
        logger.info('Calculating reaction profile')

        @work_in(self.name)
        def calculate(reaction):
            reaction.find_lowest_energy_conformers()
            reaction.optimise_reacs_prods()
            reaction.find_complexes()
            reaction.locate_transition_state()
            reaction.find_lowest_energy_ts_conformer()
            reaction.calculate_single_points()
            return None

        calculate(self)

        if not with_complexes:
            plot_reaction_profile([self], units=units, name=self.name)

        if with_complexes:
            self._plot_reaction_profile_with_complexes(units=units)

        return None
Beispiel #2
0
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
Beispiel #3
0
def test_free_energy_profile():

    # Use a spoofed Gaussian09 and XTB install
    Config.lcode = 'xtb'

    Config.hcode = 'g09'
    Config.G09.path = here

    Config.ts_template_folder_path = os.getcwd()

    method = get_hmethod()
    assert method.name == 'g09'

    rxn = reaction.Reaction(Reactant(name='F-', smiles='[F-]'),
                            Reactant(name='CH3Cl', smiles='ClC'),
                            Product(name='Cl-', smiles='[Cl-]'),
                            Product(name='CH3F', smiles='CF'),
                            name='sn2',
                            solvent_name='water')

    # Don't run the calculation without a working XTB install
    if shutil.which('xtb') is None or not shutil.which('xtb').endswith('xtb'):
        return

    rxn.calculate_reaction_profile(free_energy=True)

    # Allow ~0.5 kcal mol-1 either side of the true value

    dg_ts = rxn.calc_delta_g_ddagger()
    assert 17 < dg_ts * Constants.ha2kcalmol < 18

    dg_r = rxn.calc_delta_g()
    assert -13.2 < dg_r * Constants.ha2kcalmol < -12.3

    dh_ts = rxn.calc_delta_h_ddagger()
    assert 9.2 < dh_ts * Constants.ha2kcalmol < 10.3

    dh_r = rxn.calc_delta_h()
    assert -13.6 < dh_r * Constants.ha2kcalmol < -12.6

    # Should be able to plot an enthalpy profile
    plot_reaction_profile([rxn], units=KcalMol, name='enthalpy', enthalpy=True)
    assert os.path.exists('enthalpy_reaction_profile.png')
    os.remove('enthalpy_reaction_profile.png')

    # Reset the configuration to the default values
    Config.hcode = None
    Config.G09.path = None
    Config.lcode = None
    Config.XTB.path = None
Beispiel #4
0
    def calculate_reaction_profile(self, units=KcalMol):
        """Calculate a multistep reaction profile using the products of step 1
        as the reactants of step 2 etc."""
        logger.info('Calculating reaction profile')
        h_method = get_hmethod()

        @work_in(self.name)
        def calculate(reaction, prev_products):

            for mol in reaction.reacs + reaction.prods:

                # If this molecule is one of the previous products don't
                # regenerate conformers
                skip_conformers = False
                for prod in prev_products:

                    # Has the same atoms & charge etc. as in the unique string
                    if str(mol) == str(prod):
                        mol = prod
                        skip_conformers = True

                if not skip_conformers:
                    mol.find_lowest_energy_conformer(
                        hmethod=h_method if Config.hmethod_conformers else None
                    )

            reaction.optimise_reacs_prods()
            reaction.find_complexes()
            reaction.locate_transition_state()
            reaction.find_lowest_energy_ts_conformer()
            reaction.calculate_single_points()

            return None

        # For all the reactions calculate the reactants, products and TS
        for i, r in enumerate(self.reactions):
            r.name = f'{self.name}_step{i}'

            if i == 0:
                # First reaction has no previous products
                calculate(reaction=r, prev_products=[])

            else:
                calculate(reaction=r,
                          prev_products=self.reactions[i - 1].prods)

        plot_reaction_profile(self.reactions, units=units, name=self.name)
        return None
Beispiel #5
0
    def calculate_reaction_profile(self,
                                   units=KcalMol,
                                   with_complexes=False,
                                   free_energy=False,
                                   enthalpy=False):
        """
        Calculate and plot a reaction profile for this reaction in some units

        Keyword Arguments:
            units (autode.units.Unit):
            with_complexes (bool): Calculate the lowest energy conformers
                                   of the reactant and product complexes
            free_energy (bool): Calculate the free energy profile (G)
            enthalpy (bool): Calculate the enthalpic profile (H)
        """
        logger.info('Calculating reaction profile')

        @work_in(self.name)
        def calculate(reaction):
            reaction.find_lowest_energy_conformers()
            reaction.optimise_reacs_prods()
            reaction.find_complexes()
            reaction.locate_transition_state()
            reaction.find_lowest_energy_ts_conformer()
            if with_complexes:
                reaction.calculate_complexes()
            # Calculate both G and H if either are requested
            if free_energy or enthalpy:
                reaction.calculate_thermochemical_cont()
            reaction.calculate_single_points()
            reaction.print_output()
            return None

        calculate(self)

        if not with_complexes:
            plot_reaction_profile([self],
                                  units=units,
                                  name=self.name,
                                  free_energy=free_energy,
                                  enthalpy=enthalpy)

        if with_complexes:
            self._plot_reaction_profile_with_complexes(units=units,
                                                       free_energy=free_energy,
                                                       enthalpy=enthalpy)
        return None
Beispiel #6
0
    def _plot_reaction_profile_with_complexes(self, units, free_energy,
                                              enthalpy):
        """Plot a reaction profile with the association complexes of R, P"""
        reactions_wc = []

        if free_energy or enthalpy:
            raise NotImplementedError('Significant likelihood of very low'
                                      ' frequency harmonic modes – G and H not'
                                      'implemented')
        # If the reactant complex contains more than one molecule then
        # make a reaction that is separated reactants -> reactant complex
        if len(self.reacs) > 1:
            reactant_complex = deepcopy(self.reactant)
            reactant_complex.__class__ = Product
            reactions_wc.append(
                Reaction(*self.reacs,
                         reactant_complex,
                         name='reactant_complex'))

        # The elementary reaction is then
        # reactant complex -> product complex
        reactant_complex = deepcopy(self.reactant)
        reactant_complex.__class__ = Reactant
        product_complex = deepcopy(self.product)
        product_complex.__class__ = Product

        reaction = Reaction(reactant_complex, product_complex)
        reaction.ts = self.ts

        reactions_wc.append(reaction)

        # As with the product complex add the dissociation of the product
        # complex into it's separated components
        if len(self.prods) > 1:
            product_complex = deepcopy(self.product)
            product_complex.__class__ = Reactant
            reactions_wc.append(
                Reaction(*self.prods, product_complex, name='product_complex'))

        plot_reaction_profile(reactions=reactions_wc,
                              units=units,
                              name=self.name,
                              free_energy=free_energy,
                              enthalpy=enthalpy)
        return None
Beispiel #7
0
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')
Beispiel #8
0
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)
Beispiel #9
0
    def _plot_reaction_profile_with_complexes(self, units):
        @work_in(self.name)
        def calculate_complexes():
            return self.calculate_complexes()

        calculate_complexes()

        reactions_wc = []

        # If the reactant complex contains more than one molecule then
        # make a reaction that is separated reactants -> reactant complex
        if len(self.reacs) > 1:
            reactant_complex = deepcopy(self.reactant)
            reactant_complex.__class__ = Product
            reactions_wc.append(
                Reaction(*self.reacs,
                         reactant_complex,
                         name='reactant_complex'))

        # The elementary reaction is then
        # reactant complex -> product complex
        reactant_complex = deepcopy(self.reactant)
        reactant_complex.__class__ = Reactant
        product_complex = deepcopy(self.product)
        product_complex.__class__ = Product

        reaction = Reaction(reactant_complex, product_complex)
        reaction.ts = self.ts

        reactions_wc.append(reaction)

        # As with the product complex add the dissociation of the product
        # complex into it's separated components
        if len(self.prods) > 1:
            product_complex = deepcopy(self.product)
            product_complex.__class__ = Reactant
            reactions_wc.append(
                Reaction(*self.prods, product_complex, name='product_complex'))

        plot_reaction_profile(reactions=reactions_wc,
                              units=units,
                              name=self.name)

        return None