예제 #1
0
def parse_smiles(smiles):
    """Parse the given smiles string
    Args:
        smiles (str): smiles string to be parsed
    """

    logger.info(f'Parsing SMILES string: {smiles}')

    parser = SmilesParser()

    for char, char_type in divide_smiles(smiles):
        parser.analyse_char(char, char_type)

    if len(parser.ring_dict) != 0:
        # This means a ring number has only been mentioned once, which is
        # invalid
        logger.critical('Invalid SMILES string')
        raise InvalidSmilesString

    parser.add_hs()

    parser.charge = sum(parser.charge_dict.values())

    parser.analyse_alkene_stereochem_dict()

    for atom_no in sorted(parser.stereochem_dict.keys()):
        parser.add_stereochem(atom_no)

    return parser
예제 #2
0
    def _check_solvent(self):
        """Check that all the solvents are the same for reactants and products
        """
        molecules = self.reacs + self.prods

        if self.solvent is None:
            if all([mol.solvent is None for mol in molecules]):
                logger.info('Reaction is in the gas phase')
                return

            elif all([mol.solvent is not None for mol in molecules]):
                if not all([mol.solvent == self.reacs[0].solvent for mol in molecules]):
                    logger.critical('Solvents in reactants and products '
                                    'don\'t match')
                    raise SolventsDontMatch

                else:
                    logger.info(f'Setting the reaction solvent to '
                                f'{self.reacs[0].solvent}')
                    self.solvent = self.reacs[0].solvent

            else:
                logger.critical('Some species solvated and some not!')
                raise SolventsDontMatch

        if self.solvent is not None:
            logger.info(f'Setting solvent to {self.solvent.name} for all '
                        f'molecules in the reaction')

            for mol in self.reacs + self.prods:
                mol.solvent = self.solvent

        logger.info(f'Set the solvent of all species in the reaction to '
                    f'{self.solvent.name}')
        return None
예제 #3
0
    def _check_balance(self):
        """Check that the number of atoms and charge balances between reactants
         and products. If they don't exit
        immediately
        """
        def total(molecules, attr):
            return sum([getattr(m, attr) for m in molecules])

        if total(self.reacs, 'n_atoms') != total(self.prods, 'n_atoms'):
            logger.critical('Number of atoms doesn\'t balance')
            raise UnbalancedReaction

        if total(self.reacs, 'charge') != total(self.prods, 'charge'):
            logger.critical('Charge doesn\'t balance')
            raise UnbalancedReaction

        # Ensure the number of unpaired electrons is equal on the left and
        # right-hand sides of the reaction, for now
        if (total(self.reacs, 'mult') - len(self.reacs) !=
                total(self.prods, 'mult') - len(self.prods)):
            raise NotImplementedError('Found a change in spin state – not '
                                      'implemented yet!')

        self.charge = total(self.reacs, 'charge')
        return None
예제 #4
0
파일: methods.py 프로젝트: dpregeljc/autodE
def get_defined_method(name, possibilities):
    """
    Get an electronic structure method defined by it's name

    Arguments:
        name (str):
        possibilities (list(autode.wrappers.base.ElectronicStructureMethod)):

    Returns:
        (autode.wrappers.base.ElectronicStructureMethod): Method
    """

    for method in possibilities:
        if method.name == name:

            method.set_availability()
            if method.available:
                return method

            else:
                logger.critical('Electronic structure method is not available')
                raise MethodUnavailable

    logger.critical('Requested electronic structure code doesn\'t exist')
    raise MethodUnavailable
예제 #5
0
def get_keywords(calc_input, molecule):
    """Generate a keywords list and adding solvent"""

    new_keywords = []
    scf_block = False

    for keyword in calc_input.keywords:

        if isinstance(keyword, kws.Functional):
            keyword = f'dft\n  maxiter 100\n  xc {keyword.nwchem}\nend'

        elif isinstance(keyword, kws.BasisSet):
            keyword = f'basis\n  *   library {keyword.nwchem}\nend'

        elif isinstance(keyword, kws.Keyword):
            keyword = keyword.nwchem

        if 'opt' in keyword.lower() and molecule.n_atoms == 1:
            logger.warning('Cannot do an optimisation for a single atom')

            # Replace any 'opt' containing word in this keyword with energy
            words = []
            for word in keyword.split():
                if 'opt' in word:
                    words.append('energy')
                else:
                    words.append(word)

            new_keywords.append(' '.join(words))

        elif keyword.lower().startswith('dft'):
            lines = keyword.split('\n')
            lines.insert(1, f'  mult {molecule.mult}')
            new_keyword = '\n'.join(lines)
            new_keywords.append(new_keyword)

        elif keyword.lower().startswith('scf'):
            if calc_input.solvent:
                logger.critical('nwchem only supports solvent for DFT calcs')
                raise UnsuppportedCalculationInput

            scf_block = True
            lines = keyword.split('\n')
            lines.insert(1, f'  nopen {molecule.mult - 1}')
            new_keyword = '\n'.join(lines)
            new_keywords.append(new_keyword)

        elif (any(st in keyword.lower() for st in ['ccsd', 'mp2'])
              and not scf_block):

            if calc_input.solvent.keyword is not None:
                logger.critical('nwchem only supports solvent for DFT calcs')
                raise UnsuppportedCalculationInput

            new_keywords.append(f'scf\n  nopen {molecule.mult - 1}\nend')
            new_keywords.append(keyword)
        else:
            new_keywords.append(keyword)

    return new_keywords
예제 #6
0
파일: methods.py 프로젝트: gabegomes/autodE
def get_first_available_method(possibilities):
    """
    Get the first electronic structure method that is available in a list of
    possibilities

    Arguments:
        possibilities (list(autode.wrappers.base.ElectronicStructureMethod)):

    Returns:
        (autode.wrappers.base.ElectronicStructureMethod): Method
    """
    for method in possibilities:

        if method.available:
            return method

    logger.critical('No electronic structure methods available')
    raise MethodUnavailable
예제 #7
0
    def _check_balance(self):
        """Check that the number of atoms and charge balances between reactants
         and products. If they don't exit
        immediately
        """
        def total(molecules, attr):
            return sum([getattr(m, attr) for m in molecules])

        if total(self.reacs, 'n_atoms') != total(self.prods, 'n_atoms'):
            logger.critical('Number of atoms doesn\'t balance')
            raise UnbalancedReaction

        if total(self.reacs, 'charge') != total(self.prods, 'charge'):
            logger.critical('Charge doesn\'t balance')
            raise UnbalancedReaction

        self.charge = total(self.reacs, 'charge')
        return None
예제 #8
0
def get_keywords(calc_input, molecule):
    """Get the keywords to use for a MOPAC calculation"""
    # To determine if there is an optimisation or single point the keywords
    # needs to be a subclass of Keywords
    assert isinstance(calc_input.keywords, Keywords)

    keywords = deepcopy(calc_input.keywords)
    if isinstance(calc_input.keywords, SinglePointKeywords):
        # Single point calculation add the 1SCF keyword to prevent opt
        if not any('1scf' in kw.lower() for kw in keywords):
            keywords.append('1SCF')

    if isinstance(calc_input.keywords, GradientKeywords):
        # Gradient calculation needs GRAD
        if not any('grad' in kw.lower() for kw in keywords):
            keywords.append('GRAD')

        # Gradient calculation add the 1SCF keyword to prevent opt
        if not any('1scf' in kw.lower() for kw in keywords):
            keywords.append('1SCF')

    if calc_input.point_charges is not None:
        keywords.append('QMMM')

    if calc_input.solvent is not None:
        dielectric = solvents_and_dielectrics[calc_input.solvent]
        keywords.append(f'EPS={dielectric}')

    # Add the charge and multiplicity
    keywords.append(f'CHARGE={molecule.charge}')

    if molecule.mult != 1:
        if molecule.mult == 2:
            keywords.append('DOUBLET')
        elif molecule.mult == 3:
            keywords.append('OPEN(2,2)')
        else:
            logger.critical('Unsupported spin multiplicity')
            raise UnsuppportedCalculationInput

    return keywords
예제 #9
0
def classify(reactants, products):

    if len(reactants) == 2 and len(products) == 1:
        logger.info('Classifying reaction as addition')
        return Addition

    elif len(reactants) == 1 and len(products) in [2, 3]:
        logger.info('Classifying reaction as dissociation')
        return Dissociation

    elif len(reactants) == 2 and len(products) == 2:
        logger.info('Classifying reaction as substitution')
        return Substitution

    elif len(reactants) == 2 and len(products) == 3:
        logger.info('Classifying reaction as elimination')
        return Elimination

    elif len(reactants) == 1 and len(products) == 1:
        logger.info('Classifying reaction as rearrangement')
        return Rearrangement

    elif len(reactants) == 0:
        logger.critical('Reaction had no reactants – cannot form a reaction')
        raise ReactionFormationFalied

    elif len(products) == 0:
        logger.critical('Reaction had no products – cannot form a reaction')
        raise ReactionFormationFalied

    else:
        logger.critical('Unsupported reaction type')
        raise NotImplementedError