Example #1
0
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.bond = GroupBond(None, None, order=['D'])
     self.orderList = [['S'], ['D'], ['T'], ['B'], ['S', 'D'], ['D', 'S'],
                       ['D', 'T'], ['S', 'D', 'T']]
Example #2
0
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.bond = GroupBond(None, None, order=[2])
     self.orderList = [[1], [2], [3], [1.5], [1, 2], [2, 1], [2, 3],
                       [1, 2, 3]]
 def testApplyActionDecrementBond(self):
     """
     Test the GroupBond.applyAction() method for a CHANGE_BOND action.
     """
     action = ["CHANGE_BOND", "*1", -1, "*2"]
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
         except ActionError:
             self.assertTrue("S" in order0 or "B" in order0)
Example #4
0
 def testIsSpecificCaseOf(self):
     """
     Test the GroupBond.isSpecificCaseOf() method.
     """
     for order1 in self.orderList:
         for order2 in self.orderList:
             bond1 = GroupBond(None, None, order=order1)
             bond2 = GroupBond(None, None, order=order2)
             if order1 == order2 or all([o in order2 for o in order1]):
                 self.assertTrue(bond1.isSpecificCaseOf(bond2))
             else:
                 self.assertFalse(bond1.isSpecificCaseOf(bond2))
Example #5
0
 def testIsSpecificCaseOf(self):
     """
     Test the GroupBond.isSpecificCaseOf() method.
     """
     for order1 in self.orderList:
         for order2 in self.orderList:
             bond1 = GroupBond(None, None, order=order1)
             bond2 = GroupBond(None, None, order=order2)
             if order1 == order2 or all([o in order2 for o in order1]):
                 self.assertTrue(bond1.isSpecificCaseOf(bond2))
             else:
                 self.assertFalse(bond1.isSpecificCaseOf(bond2))
Example #6
0
 def testApplyActionDecrementBond(self):
     """
     Test the GroupBond.applyAction() method for a CHANGE_BOND action.
     """
     action = ['CHANGE_BOND', '*1', -1, '*2']
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
         except ActionError:
             self.assertTrue(1 in order0 or 1.5 in order0)
Example #7
0
 def testApplyActionDecrementBond(self):
     """
     Test the GroupBond.applyAction() method for a CHANGE_BOND action.
     """
     action = ['CHANGE_BOND', '*1', -1, '*2']
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
         except ActionError:
             self.assertTrue(1 in order0 or 1.5 in order0)
Example #8
0
 def testApplyActionLoseRadical(self):
     """
     Test the GroupBond.applyAction() method for a LOSE_RADICAL action.
     """
     action = ['LOSE_RADICAL', '*1', 1]
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
             self.fail('GroupBond.applyAction() unexpectedly processed a LOSE_RADICAL action.')
         except ActionError:
             pass
Example #9
0
 def testApplyActionFormBond(self):
     """
     Test the GroupBond.applyAction() method for a FORM_BOND action.
     """
     action = ['FORM_BOND', '*1', 1, '*2']
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
             self.fail('GroupBond.applyAction() unexpectedly processed a FORM_BOND action.')
         except ActionError:
             pass
 def testApplyActionGainRadical(self):
     """
     Test the GroupBond.applyAction() method for a GAIN_RADICAL action.
     """
     action = ["GAIN_RADICAL", "*1", 1]
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
             self.fail("GroupBond.applyAction() unexpectedly processed a GAIN_RADICAL action.")
         except ActionError:
             pass
 def testApplyActionBreakBond(self):
     """
     Test the GroupBond.applyAction() method for a BREAK_BOND action.
     """
     action = ["BREAK_BOND", "*1", "S", "*2"]
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
             self.fail("GroupBond.applyAction() unexpectedly processed a BREAK_BOND action.")
         except ActionError:
             pass
Example #12
0
 def testApplyActionFormBond(self):
     """
     Test the GroupBond.applyAction() method for a FORM_BOND action.
     """
     action = ['FORM_BOND', '*1', 1, '*2']
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
             self.fail(
                 'GroupBond.applyAction() unexpectedly processed a FORM_BOND action.'
             )
         except ActionError:
             pass
Example #13
0
 def testApplyActionLoseRadical(self):
     """
     Test the GroupBond.applyAction() method for a LOSE_RADICAL action.
     """
     action = ['LOSE_RADICAL', '*1', 1]
     for order0 in self.orderList:
         bond0 = GroupBond(None, None, order=order0)
         bond = bond0.copy()
         try:
             bond.applyAction(action)
             self.fail(
                 'GroupBond.applyAction() unexpectedly processed a LOSE_RADICAL action.'
             )
         except ActionError:
             pass
Example #14
0
 def testEquivalent(self):
     """
     Test the GroupBond.equivalent() method.
     """
     for order1 in self.orderList:
         for order2 in self.orderList:
             bond1 = GroupBond(None, None, order=order1)
             bond2 = GroupBond(None, None, order=order2)
             if order1 == order2 or (all([o in order2 for o in order1])
                                     and all([o in order1
                                              for o in order2])):
                 self.assertTrue(bond1.equivalent(bond2))
                 self.assertTrue(bond2.equivalent(bond1))
             else:
                 self.assertFalse(bond1.equivalent(bond2))
                 self.assertFalse(bond2.equivalent(bond1))
Example #15
0
 def testEquivalent(self):
     """
     Test the GroupBond.equivalent() method.
     """
     for order1 in self.orderList:
         for order2 in self.orderList:
             bond1 = GroupBond(None, None, order=order1)
             bond2 = GroupBond(None, None, order=order2)
             if order1 == order2 or (all([o in order2 for o in order1]) and all([o in order1 for o in order2])):
                 self.assertTrue(bond1.equivalent(bond2))
                 self.assertTrue(bond2.equivalent(bond1))
             else:
                 self.assertFalse(bond1.equivalent(bond2))
                 self.assertFalse(bond2.equivalent(bond1))
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.bond = GroupBond(None, None, order=["D"])
     self.orderList = [["S"], ["D"], ["T"], ["B"], ["S", "D"], ["D", "S"], ["D", "T"], ["S", "D", "T"]]
Example #17
0
 def testGetOrderStr(self):
     """
     test the Bond.getOrderStr() method
     """
     bond = GroupBond(None, None, order=[1, 2, 3, 1.5])
     self.assertEqual(bond.getOrderStr(), ['S', 'D', 'T', 'B'])
Example #18
0
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.bond = GroupBond(None, None, order=[2])
     self.orderList = [[1], [2], [3], [1.5], [1,2], [2,1], [2,3], [1,2,3]]
Example #19
0
class TestGroupBond(unittest.TestCase):
    """
    Contains unit tests of the GroupBond class.
    """
    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.bond = GroupBond(None, None, order=[2])
        self.orderList = [[1], [2], [3], [1.5], [1, 2], [2, 1], [2, 3],
                          [1, 2, 3]]

    def testGetOrderStr(self):
        """
        test the Bond.getOrderStr() method
        """
        bond = GroupBond(None, None, order=[1, 2, 3, 1.5])
        self.assertEqual(bond.getOrderStr(), ['S', 'D', 'T', 'B'])

    def testSetOrderStr(self):
        """
        test the Bond.setOrderStr() method
        """

        self.bond.setOrderStr(["B", 'T'])
        self.assertEqual(set(self.bond.order), set([3, 1.5]))

    def testGetOrderNum(self):
        """
        test the Bond.getOrderNum() method
        """
        self.assertEqual(self.bond.getOrderNum(), [2])

    def testSetOrderNum(self):
        """
        test the Bond.setOrderNum() method
        """

        self.bond.setOrderNum([3, 1, 2])
        self.assertEqual(self.bond.getOrderStr(), ['T', 'S', 'D'])

    def testApplyActionBreakBond(self):
        """
        Test the GroupBond.applyAction() method for a BREAK_BOND action.
        """
        action = ['BREAK_BOND', '*1', 1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail(
                    'GroupBond.applyAction() unexpectedly processed a BREAK_BOND action.'
                )
            except ActionError:
                pass

    def testApplyActionFormBond(self):
        """
        Test the GroupBond.applyAction() method for a FORM_BOND action.
        """
        action = ['FORM_BOND', '*1', 1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail(
                    'GroupBond.applyAction() unexpectedly processed a FORM_BOND action.'
                )
            except ActionError:
                pass

    def testApplyActionIncrementBond(self):
        """
        Test the GroupBond.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', 1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
            except ActionError:
                self.assertTrue(3 in order0 or 1.5 in order0)

    def testApplyActionDecrementBond(self):
        """
        Test the GroupBond.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', -1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
            except ActionError:
                self.assertTrue(1 in order0 or 1.5 in order0)

    def testApplyActionGainRadical(self):
        """
        Test the GroupBond.applyAction() method for a GAIN_RADICAL action.
        """
        action = ['GAIN_RADICAL', '*1', 1]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail(
                    'GroupBond.applyAction() unexpectedly processed a GAIN_RADICAL action.'
                )
            except ActionError:
                pass

    def testApplyActionLoseRadical(self):
        """
        Test the GroupBond.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail(
                    'GroupBond.applyAction() unexpectedly processed a LOSE_RADICAL action.'
                )
            except ActionError:
                pass

    def testEquivalent(self):
        """
        Test the GroupBond.equivalent() method.
        """
        for order1 in self.orderList:
            for order2 in self.orderList:
                bond1 = GroupBond(None, None, order=order1)
                bond2 = GroupBond(None, None, order=order2)
                if order1 == order2 or (all([o in order2 for o in order1])
                                        and all([o in order1
                                                 for o in order2])):
                    self.assertTrue(bond1.equivalent(bond2))
                    self.assertTrue(bond2.equivalent(bond1))
                else:
                    self.assertFalse(bond1.equivalent(bond2))
                    self.assertFalse(bond2.equivalent(bond1))

    def testIsSpecificCaseOf(self):
        """
        Test the GroupBond.isSpecificCaseOf() method.
        """
        for order1 in self.orderList:
            for order2 in self.orderList:
                bond1 = GroupBond(None, None, order=order1)
                bond2 = GroupBond(None, None, order=order2)
                if order1 == order2 or all([o in order2 for o in order1]):
                    self.assertTrue(bond1.isSpecificCaseOf(bond2))
                else:
                    self.assertFalse(bond1.isSpecificCaseOf(bond2))

    def testCopy(self):
        """
        Test the GroupBond.copy() method.
        """
        bond = self.bond.copy()
        self.assertEqual(len(self.bond.order), len(bond.order))
        self.assertEqual(self.bond.order, bond.order)

    def testPickle(self):
        """
        Test that a GroupBond object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle
        bond = cPickle.loads(cPickle.dumps(self.bond))
        self.assertEqual(len(self.bond.order), len(bond.order))
        self.assertEqual(self.bond.order, bond.order)
Example #20
0
 def testGetOrderStr(self):
     """
     test the Bond.getOrderStr() method
     """
     bond = GroupBond(None,None,order = [1,2,3,1.5])
     self.assertEqual(bond.getOrderStr(),['S','D','T','B'])
                atom.props['inRing'] = True
        # Label atoms
        atoms[0].label = '*1'
        if a > 1:
            atoms[1].label = '*4'
        if a > 2:
            atoms[-1].label = '*5'
        if a > 3:
            for i, atom in enumerate(atoms[2:-1]):
                atom.label = '*{0}'.format(i + 6)
        # Add to group
        for atom in reversed(atoms):
            new_group.addAtom(atom)
        # Add bond to newly added atoms
        new_group.addBond(
            GroupBond(new_group.atoms[1], atoms[-1], order=[1, 2, 3, 1.5]))
        # Create bonds
        if a > 1:
            bonds = [
                GroupBond(atoms[i], atoms[i + 1], order=[1, 2, 3, 1.5])
                for i in range(a - 1)
            ]
            for bond in bonds:
                new_group.addBond(bond)

    # Add remaining atoms
    if b > 0:
        # Create atoms
        atoms = [
            GroupAtom(atomType=['R!H'],
                      radicalElectrons=None,
Example #22
0
class TestGroupBond(unittest.TestCase):
    """
    Contains unit tests of the GroupBond class.
    """

    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.bond = GroupBond(None, None, order=[2])
        self.orderList = [[1], [2], [3], [1.5], [1,2], [2,1], [2,3], [1,2,3]]
    
    def testGetOrderStr(self):
        """
        test the Bond.getOrderStr() method
        """
        bond = GroupBond(None,None,order = [1,2,3,1.5])
        self.assertEqual(bond.getOrderStr(),['S','D','T','B'])
        
    def testSetOrderStr(self):
        """
        test the Bond.setOrderStr() method
        """
        
        self.bond.setOrderStr(["B",'T'])
        self.assertEqual(set(self.bond.order), set([3,1.5]))
    
    def testGetOrderNum(self):
        """
        test the Bond.getOrderNum() method
        """
        self.assertEqual(self.bond.getOrderNum(),[2])
        
    def testSetOrderNum(self):
        """
        test the Bond.setOrderNum() method
        """
        
        self.bond.setOrderNum([3,1,2])
        self.assertEqual(self.bond.getOrderStr(),['T','S','D'])
    
    
    def testApplyActionBreakBond(self):
        """
        Test the GroupBond.applyAction() method for a BREAK_BOND action.
        """
        action = ['BREAK_BOND', '*1', 1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail('GroupBond.applyAction() unexpectedly processed a BREAK_BOND action.')
            except ActionError:
                pass
    
    def testApplyActionFormBond(self):
        """
        Test the GroupBond.applyAction() method for a FORM_BOND action.
        """
        action = ['FORM_BOND', '*1', 1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail('GroupBond.applyAction() unexpectedly processed a FORM_BOND action.')
            except ActionError:
                pass
    
    def testApplyActionIncrementBond(self):
        """
        Test the GroupBond.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', 1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
            except ActionError:
                self.assertTrue(3 in order0 or 1.5 in order0)
                
    def testApplyActionDecrementBond(self):
        """
        Test the GroupBond.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', -1, '*2']
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
            except ActionError:
                self.assertTrue(1 in order0 or 1.5 in order0)
            
    def testApplyActionGainRadical(self):
        """
        Test the GroupBond.applyAction() method for a GAIN_RADICAL action.
        """
        action = ['GAIN_RADICAL', '*1', 1]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail('GroupBond.applyAction() unexpectedly processed a GAIN_RADICAL action.')
            except ActionError:
                pass
    
    def testApplyActionLoseRadical(self):
        """
        Test the GroupBond.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail('GroupBond.applyAction() unexpectedly processed a LOSE_RADICAL action.')
            except ActionError:
                pass
    
    def testEquivalent(self):
        """
        Test the GroupBond.equivalent() method.
        """
        for order1 in self.orderList:
            for order2 in self.orderList:
                bond1 = GroupBond(None, None, order=order1)
                bond2 = GroupBond(None, None, order=order2)
                if order1 == order2 or (all([o in order2 for o in order1]) and all([o in order1 for o in order2])):
                    self.assertTrue(bond1.equivalent(bond2))
                    self.assertTrue(bond2.equivalent(bond1))
                else:
                    self.assertFalse(bond1.equivalent(bond2))
                    self.assertFalse(bond2.equivalent(bond1))
    
    def testIsSpecificCaseOf(self):
        """
        Test the GroupBond.isSpecificCaseOf() method.
        """
        for order1 in self.orderList:
            for order2 in self.orderList:
                bond1 = GroupBond(None, None, order=order1)
                bond2 = GroupBond(None, None, order=order2)
                if order1 == order2 or all([o in order2 for o in order1]):
                    self.assertTrue(bond1.isSpecificCaseOf(bond2))
                else:
                    self.assertFalse(bond1.isSpecificCaseOf(bond2))
                
    def testCopy(self):
        """
        Test the GroupBond.copy() method.
        """
        bond = self.bond.copy()
        self.assertEqual(len(self.bond.order), len(bond.order))
        self.assertEqual(self.bond.order, bond.order)
    
    def testPickle(self):
        """
        Test that a GroupBond object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle
        bond = cPickle.loads(cPickle.dumps(self.bond))
        self.assertEqual(len(self.bond.order), len(bond.order))
        self.assertEqual(self.bond.order, bond.order)
Example #23
0
def from_old_adjacency_list(adjlist, group=False, saturate_h=False):
    """
    Convert a pre-June-2014 string adjacency list `adjlist` into a set of :class:`Atom` and
    :class:`Bond` objects. 
    It can read both "old style" that existed for years, an the "intermediate style" that
    existed for a few months in 2014, with the extra column of integers for lone pairs.
    """
    atoms = []
    atomdict = {}
    bonds = {}

    try:
        adjlist = adjlist.strip()
        lines = adjlist.splitlines()
        if adjlist == '' or len(lines) == 0:
            raise InvalidAdjacencyListError('Empty adjacency list.')

        # Skip the first line if it contains a label
        if len(lines[0].split()) == 1:
            label = lines.pop(0)
            if len(lines) == 0:
                raise InvalidAdjacencyListError(
                    """Error in adjacency list\n{0}\nNo atoms specified.""".
                    format(adjlist))

        mistake1 = re.compile(r'\{[^}]*\s+[^}]*\}')
        atomic_multiplicities = {
        }  # these are no longer stored on atoms, so we make a separate dictionary
        # Iterate over the remaining lines, generating Atom or GroupAtom objects
        for line in lines:

            # Sometimes people put spaces after commas, which messes up the
            # parse-by-whitespace. Examples include '{Cd, Ct}'.
            if mistake1.search(line):
                raise InvalidAdjacencyListError(
                    "Error in adjacency list: \n{1}\nspecies shouldn't have spaces inside "
                    "braces: {0}".format(
                        mistake1.search(line).group(), adjlist))

            # Sometimes commas are used to delimit bonds in the bond list,
            # so replace them just in case
            line = line.replace('},{', '} {')

            data = line.split()

            # Skip if blank line
            if len(data) == 0:
                continue

            # First item is index for atom
            # Sometimes these have a trailing period (as if in a numbered list),
            # so remove it just in case
            aid = int(data[0].strip('.'))

            # If second item starts with '*', then atom is labeled
            label = ''
            index = 1
            if data[1][0] == '*':
                label = data[1]
                index += 1

            # Next is the element or functional group element
            # A list can be specified with the {,} syntax
            atom_type = data[index]
            if atom_type[0] == '{':
                atom_type = atom_type[1:-1].split(',')
            else:
                atom_type = [atom_type]
            index += 1

            # Next is the electron state
            radical_electrons = []
            additional_lone_pairs = []
            elec_state = data[index].upper()
            if elec_state[0] == '{':
                elec_state = elec_state[1:-1].split(',')
            else:
                elec_state = [elec_state]
            if len(elec_state) == 0:
                raise InvalidAdjacencyListError(
                    "Error in adjacency list:\n{0}\nThere must be some electronic state defined for an "
                    "old adjlist".format(adjlist))
            for e in elec_state:
                if e == '0':
                    radical_electrons.append(0)
                    additional_lone_pairs.append(0)
                elif e == '1':
                    radical_electrons.append(1)
                    additional_lone_pairs.append(0)
                elif e == '2':
                    if not group:
                        raise InvalidAdjacencyListError(
                            "Error in adjacency list:\n{0}\nNumber of radical electrons = 2 is not specific enough. "
                            "Please use 2S or 2T.".format(adjlist))
                    # includes 2S and 2T
                    radical_electrons.append(0)
                    additional_lone_pairs.append(1)
                    radical_electrons.append(2)
                    additional_lone_pairs.append(0)
                elif e == '2S':
                    radical_electrons.append(0)
                    additional_lone_pairs.append(1)
                elif e == '2T':
                    radical_electrons.append(2)
                    additional_lone_pairs.append(0)
                elif e == '3':
                    if not group:
                        raise InvalidAdjacencyListError(
                            "Error in adjacency list:\n{0}\nNumber of radical electrons = 3 is not specific enough. "
                            "Please use 3D or 3Q.".format(adjlist))
                    # includes 3D and 3Q
                    radical_electrons.append(1)
                    additional_lone_pairs.append(1)
                    radical_electrons.append(3)
                    additional_lone_pairs.append(0)
                elif e == '3D':
                    radical_electrons.append(1)
                    additional_lone_pairs.append(1)
                elif e == '3Q':
                    radical_electrons.append(3)
                    additional_lone_pairs.append(0)
                elif e == '4':
                    if not group:
                        raise InvalidAdjacencyListError(
                            "Error in adjacency list:\n{0}\nNumber of radical electrons = 4 is not specific enough. "
                            "Please use 4S, 4T, or 4V.".format(adjlist))
                    # includes 4S, 4T, and 4V
                    radical_electrons.append(0)
                    additional_lone_pairs.append(2)
                    radical_electrons.append(2)
                    additional_lone_pairs.append(1)
                    radical_electrons.append(4)
                    additional_lone_pairs.append(0)
                elif e == '4S':
                    radical_electrons.append(0)
                    additional_lone_pairs.append(2)
                elif e == '4T':
                    radical_electrons.append(2)
                    additional_lone_pairs.append(1)
                elif e == '4V':
                    radical_electrons.append(4)
                    additional_lone_pairs.append(0)
                elif e == 'X':
                    if not group:
                        raise InvalidAdjacencyListError(
                            "Error in adjacency list:\n{0}\nNumber of radical electrons = X is not specific enough. "
                            "Wildcards should only be used for groups.".format(
                                adjlist))
                    radical_electrons = []
            index += 1

            # Next number defines the number of lone electron pairs (if provided)
            lone_pairs_of_electrons = None
            if len(data) > index:
                lp_state = data[index]
                if lp_state[0] == '{':
                    # this is the start of the chemical bonds - no lone pair info was provided
                    lone_pairs_of_electrons = None
                else:
                    if lp_state == '0':
                        lone_pairs_of_electrons = 0
                    if lp_state == '1':
                        lone_pairs_of_electrons = 1
                    if lp_state == '2':
                        lone_pairs_of_electrons = 2
                    if lp_state == '3':
                        lone_pairs_of_electrons = 3
                    if lp_state == '4':
                        lone_pairs_of_electrons = 4
                    index += 1
            else:  # no bonds or lone pair info provided.
                lone_pairs_of_electrons = None

            # Create a new atom based on the above information
            if group:
                if lone_pairs_of_electrons is not None:
                    lone_pairs_of_electrons = [
                        additional + lone_pairs_of_electrons
                        for additional in additional_lone_pairs
                    ]
                atom = GroupAtom(
                    atomtype=atom_type,
                    radical_electrons=sorted(set(radical_electrons)),
                    charge=None,
                    label=label,
                    lone_pairs=lone_pairs_of_electrons,
                    # Assign lone_pairs_of_electrons as None if it is not explicitly provided
                )

            else:
                if lone_pairs_of_electrons is not None:
                    # Intermediate adjlist representation
                    lone_pairs_of_electrons = lone_pairs_of_electrons + additional_lone_pairs[
                        0]
                else:
                    # Add the standard number of lone pairs with the additional lone pairs
                    lone_pairs_of_electrons = PeriodicSystem.lone_pairs[
                        atom_type[0]] + additional_lone_pairs[0]

                atom = Atom(
                    element=atom_type[0],
                    radical_electrons=radical_electrons[0],
                    charge=0,
                    label=label,
                    lone_pairs=lone_pairs_of_electrons,
                )
            # Add the atom to the list
            atoms.append(atom)
            atomdict[aid] = atom

            # Process list of bonds
            bonds[aid] = {}
            for datum in data[index:]:

                # Sometimes commas are used to delimit bonds in the bond list,
                # so strip them just in case
                datum = datum.strip(',')

                aid2, comma, order = datum[1:-1].partition(',')
                aid2 = int(aid2)
                if aid == aid2:
                    raise InvalidAdjacencyListError(
                        'Error in adjacency list:\n{1}\nAttempted to create a bond between '
                        'atom {0:d} and itself.'.format(aid, adjlist))

                if order[0] == '{':
                    order = order[1:-1].split(',')
                else:
                    order = [order]

                bonds[aid][aid2] = order

        # Check consistency using bonddict
        for atom1 in bonds:
            for atom2 in bonds[atom1]:
                if atom2 not in bonds:
                    raise InvalidAdjacencyListError(
                        'Error in adjacency list:\n{1}\nAtom {0:d} not in bond dictionary.'
                        .format(atom2, adjlist))
                elif atom1 not in bonds[atom2]:
                    raise InvalidAdjacencyListError(
                        'Error in adjacency list:\n{2}\nFound bond between {0:d} and {1:d}, '
                        'but not the reverse'.format(atom1, atom2, adjlist))
                elif bonds[atom1][atom2] != bonds[atom2][atom1]:
                    raise InvalidAdjacencyListError(
                        'Error in adjacency list: \n{4}\nFound bonds between {0:d} and {1:d}, but of different orders '
                        '"{2}" and "{3}".'.format(atom1, atom2,
                                                  bonds[atom1][atom2],
                                                  bonds[atom2][atom1],
                                                  adjlist))

        # Convert bonddict to use Atom[group] and Bond[group] objects
        atomkeys = list(atomdict.keys())
        atomkeys.sort()
        for aid1 in atomkeys:
            atomkeys2 = list(bonds[aid1].keys())
            atomkeys2.sort()
            for aid2 in atomkeys2:
                if aid1 < aid2:
                    atom1 = atomdict[aid1]
                    atom2 = atomdict[aid2]
                    order = bonds[aid1][aid2]
                    if group:
                        bond = GroupBond(atom1, atom2, order)
                    elif len(order) == 1:
                        bond = Bond(atom1, atom2, order[0])
                    else:
                        raise InvalidAdjacencyListError(
                            'Error in adjacency list:\n{0}\nMultiple bond orders specified '
                            'for an atom.'.format(adjlist))
                    atom1.edges[atom2] = bond
                    atom2.edges[atom1] = bond

        if not group:
            if saturate_h:
                # Add explicit hydrogen atoms to complete structure if desired
                new_atoms = []
                for atom in atoms:
                    try:
                        valence = PeriodicSystem.valences[atom.symbol]
                    except KeyError:
                        raise InvalidAdjacencyListError(
                            'Error in adjacency list:\n{1}\nCannot add hydrogens: Unknown '
                            'valence for atom "{0}".'.format(
                                atom.symbol, adjlist))
                    radical = atom.radical_electrons
                    order = atom.get_total_bond_order()
                    count = valence - radical - int(
                        order) - 2 * (atom.lone_pairs -
                                      PeriodicSystem.lone_pairs[atom.symbol])
                    for i in range(count):
                        a = Atom(element='H',
                                 radical_electrons=0,
                                 charge=0,
                                 label='',
                                 lone_pairs=0)
                        b = Bond(atom, a, 'S')
                        new_atoms.append(a)
                        atom.bonds[a] = b
                        a.bonds[atom] = b
                atoms.extend(new_atoms)

            # Calculate the multiplicity for the molecule and update the charges on each atom
            n_rad = 0  # total number of radical electrons
            for atom in atoms:
                atom.update_charge()
                n_rad += atom.radical_electrons
            multiplicity = n_rad + 1  # 2 s + 1, where s is the combined spin of unpaired electrons (s = 1/2 per unpaired electron)

        else:
            # Don't set a multiplicity for groups when converting from an old adjlist
            multiplicity = None

    except InvalidAdjacencyListError:
        logging.error("Troublesome adjacency list:\n" + adjlist)
        raise

    return atoms, multiplicity
Example #24
0
def from_adjacency_list(adjlist, group=False, saturate_h=False):
    """
    Convert a string adjacency list `adjlist` into a set of :class:`Atom` and
    :class:`Bond` objects.
    """
    atoms = []
    atom_dict = {}
    bonds = {}
    multiplicity = None

    adjlist = adjlist.strip()
    lines = adjlist.splitlines()
    if adjlist == '' or len(lines) == 0:
        raise InvalidAdjacencyListError('Empty adjacency list.')

    # Detect old-style adjacency lists by looking at the last line's syntax
    last_line = lines[-1].strip()
    while not last_line:  # Remove any empty lines from the end
        lines.pop()
        last_line = lines[-1].strip()
    if re_intermediate_adjlist.match(last_line):
        logging.debug(
            "adjacency list:\n{1}\nline '{0}' looks like an intermediate style "
            "adjacency list".format(last_line, adjlist))
        return from_old_adjacency_list(adjlist,
                                       group=group,
                                       saturate_h=saturate_h)
    if re_old_adjlist.match(last_line):
        logging.debug(
            "Adjacency list:\n{1}\nline '{0}' looks like an old style adjacency list"
            .format(last_line, adjlist))
        if not group:
            logging.debug("Will assume implicit H atoms")
        return from_old_adjacency_list(adjlist,
                                       group=group,
                                       saturate_h=(not group))

    # Interpret the first line if it contains a label
    if len(lines[0].split()) == 1:
        label = lines.pop(0)
        if len(lines) == 0:
            raise InvalidAdjacencyListError(
                'No atoms specified in adjacency list.')

    # Interpret the second line if it contains a multiplicity
    if lines[0].split()[0] == 'multiplicity':
        line = lines.pop(0)
        if group:
            match = re.match(
                r'\s*multiplicity\s+\[\s*(\d(?:,\s*\d)*)\s*\]\s*$', line)
            if not match:
                rematch = re.match(r'\s*multiplicity\s+x\s*$', line)
                if not rematch:
                    raise InvalidAdjacencyListError(
                        "Invalid multiplicity line '{0}'. Should be a list like "
                        "'multiplicity [1,2,3]' or a wildcard 'multiplicity x'"
                        .format(line))
            else:
                # should match "multiplicity [1]" or " multiplicity   [ 1, 2, 3 ]" or " multiplicity [1,2,3]"
                # and whatever's inside the [] (excluding leading and trailing spaces) should be captured as group 1.
                # If a wildcard is desired, this line can be omitted or replaced with 'multiplicity x'
                # Multiplicities must be only one digit (i.e. less than 10)
                # The (?:,\s*\d)* matches patters like ", 2" 0 or more times, but doesn't capture them (because of the leading ?:)
                multiplicities = match.group(1).split(',')
                multiplicity = [int(i) for i in multiplicities]
        else:
            match = re.match(r'\s*multiplicity\s+\d+\s*$', line)
            if not match:
                raise InvalidAdjacencyListError(
                    "Invalid multiplicity line '{0}'. Should be an integer like "
                    "'multiplicity 2'".format(line))
            multiplicity = int(line.split()[1])
        if len(lines) == 0:
            raise InvalidAdjacencyListError(
                'No atoms specified in adjacency list: \n{0}'.format(adjlist))

    mistake1 = re.compile(r'\{[^}]*\s+[^}]*\}')
    # Iterate over the remaining lines, generating Atom or GroupAtom objects
    for line in lines:

        # Sometimes people put spaces after commas, which messes up the
        # parse-by-whitespace. Examples include '[Cd, Ct]'.
        if mistake1.search(line):
            raise InvalidAdjacencyListError(
                "{1} Shouldn't have spaces inside braces:\n{0}".format(
                    mistake1.search(line).group(), adjlist))

        # Sometimes commas are used to delimit bonds in the bond list,
        # so replace them just in case
        line = line.replace('},{', '} {')

        data = line.split()

        # Skip if blank line
        if len(data) == 0:
            continue

        # First item is index for atom
        # Sometimes these have a trailing period (as if in a numbered list),
        # so remove it just in case
        aid = int(data[0].strip('.'))

        # If second item starts with '*', then atom is labeled
        label = ''
        index = 1
        if data[1][0] == '*':
            label = data[1]
            index += 1

        # Next is the element or functional group element
        # A list can be specified with the {,} syntax
        atom_type = data[index]
        if atom_type[0] == '[':
            if not group:
                raise InvalidAdjacencyListError(
                    "Error on:\n{0}\nA molecule should not assign more than one "
                    "atomtype per atom.".format(adjlist))
            atom_type = atom_type[1:-1].split(',')
        else:
            atom_type = [atom_type]
        index += 1

        # Next the number of unpaired electrons
        unpaired_electrons = []
        u_state = data[index]
        if u_state[0] == 'u':
            if u_state[1] == '[':
                u_state = u_state[2:-1].split(',')
            else:
                u_state = [u_state[1]]
            for u in u_state:
                if u == '0':
                    unpaired_electrons.append(0)
                elif u == '1':
                    unpaired_electrons.append(1)
                elif u == '2':
                    unpaired_electrons.append(2)
                elif u == '3':
                    unpaired_electrons.append(3)
                elif u == '4':
                    unpaired_electrons.append(4)
                elif u == 'x':
                    if not group:
                        raise InvalidAdjacencyListError(
                            "Error on:\n{0}\nA molecule should not assign a wildcard to "
                            "number of unpaired electrons.".format(adjlist))
                else:
                    raise InvalidAdjacencyListError(
                        'Number of unpaired electrons not recognized on\n{0}.'.
                        format(adjlist))
            index += 1
        else:
            raise InvalidAdjacencyListError(
                'Number of unpaired electrons not defined on\n{0}.'.format(
                    adjlist))

        # Next the number of lone electron pairs (if provided)
        lone_pairs = []
        if len(data) > index:
            lp_state = data[index]
            if lp_state[0] == 'p':
                if lp_state[1] == '[':
                    lp_state = lp_state[2:-1].split(',')
                else:
                    lp_state = [lp_state[1]]
                for lp in lp_state:
                    if lp == '0':
                        lone_pairs.append(0)
                    elif lp == '1':
                        lone_pairs.append(1)
                    elif lp == '2':
                        lone_pairs.append(2)
                    elif lp == '3':
                        lone_pairs.append(3)
                    elif lp == '4':
                        lone_pairs.append(4)
                    elif lp == 'x':
                        if not group:
                            raise InvalidAdjacencyListError(
                                "Error in adjacency list:\n{0}\nA molecule should not have "
                                "a wildcard assigned to number of lone pairs.".
                                format(adjlist))
                    else:
                        raise InvalidAdjacencyListError(
                            'Error in adjacency list:\n{0}\nNumber of lone electron pairs '
                            'not recognized.'.format(adjlist))
                index += 1
            else:
                if not group:
                    lone_pairs.append(0)
        else:
            if not group:
                lone_pairs.append(0)

        # Next the number of partial charges (if provided)
        partial_charges = []
        if len(data) > index:
            e_state = data[index]
            if e_state[0] == 'c':
                if e_state[1] == '[':
                    e_state = e_state[2:-1].split(',')
                else:
                    e_state = [e_state[1:]]
                for e in e_state:
                    if e == '0':
                        partial_charges.append(0)
                    elif e == '+1':
                        partial_charges.append(1)
                    elif e == '+2':
                        partial_charges.append(2)
                    elif e == '+3':
                        partial_charges.append(3)
                    elif e == '+4':
                        partial_charges.append(4)
                    elif e == '-1':
                        partial_charges.append(-1)
                    elif e == '-2':
                        partial_charges.append(-2)
                    elif e == '-3':
                        partial_charges.append(-3)
                    elif e == '-4':
                        partial_charges.append(-4)
                    elif e == 'x':
                        if not group:
                            raise InvalidAdjacencyListError(
                                "Error on adjacency list:\n{0}\nA molecule should not have "
                                "a wildcard assigned to number of charges.".
                                format(adjlist))
                    else:
                        raise InvalidAdjacencyListError(
                            'Error on adjacency list:\n{0}\nNumber of partial charges '
                            'not recognized.'.format(adjlist))
                index += 1
            else:
                if not group:
                    partial_charges.append(0)
        else:
            if not group:
                partial_charges.append(0)

        # Next the isotope (if provided)
        isotope = -1
        if len(data) > index:
            i_state = data[index]
            if i_state[0] == 'i':
                isotope = int(i_state[1:])
                index += 1

        # Next ring membership info (if provided)
        props = {}
        if len(data) > index:
            r_state = data[index]
            if r_state[0] == 'r':
                props['inRing'] = bool(int(r_state[1]))
                index += 1

        # Create a new atom based on the above information
        if group:
            atom = GroupAtom(atom_type, unpaired_electrons, partial_charges,
                             label, lone_pairs, props)
        else:
            atom = Atom(atom_type[0], unpaired_electrons[0],
                        partial_charges[0], label, lone_pairs[0])
            if isotope != -1:
                atom.element = get_element(atom.number, isotope)

        # Add the atom to the list
        atoms.append(atom)
        atom_dict[aid] = atom

        # Process list of bonds
        bonds[aid] = {}
        for datum in data[index:]:

            # Sometimes commas are used to delimit bonds in the bond list,
            # so strip them just in case
            datum = datum.strip(',')

            aid2, comma, order = datum[1:-1].partition(',')
            aid2 = int(aid2)
            if aid == aid2:
                raise InvalidAdjacencyListError(
                    'Error in adjacency list:\n{1}\nAttempted to create a bond between '
                    'atom {0:d} and itself.'.format(aid, adjlist))

            if order[0] == '[':
                order = order[1:-1].split(',')
            else:
                order = [order]

            bonds[aid][aid2] = order

    # Check consistency using bonddict
    for atom1 in bonds:
        for atom2 in bonds[atom1]:
            if atom2 not in bonds:
                raise InvalidAdjacencyListError(
                    'Error in adjacency list:\n{1}\nAtom {0:d} not in bond '
                    'dictionary.'.format(atom2, adjlist))
            elif atom1 not in bonds[atom2]:
                raise InvalidAdjacencyListError(
                    'Error in adjacency list:\n{2}\nFound bond between {0:d} and {1:d}, '
                    'but not the reverse.'.format(atom1, atom2, adjlist))
            elif bonds[atom1][atom2] != bonds[atom2][atom1]:
                raise InvalidAdjacencyListError(
                    'Error in adjacency list:\n{4}\nFound bonds between {0:d} and {1:d}, but of different orders '
                    '"{2}" and "{3}".'.format(atom1, atom2,
                                              bonds[atom1][atom2],
                                              bonds[atom2][atom1], adjlist))

    # Convert bonddict to use Atom[group] and Bond[group] objects
    atomkeys = list(atom_dict.keys())
    atomkeys.sort()
    for aid1 in atomkeys:
        atomkeys2 = list(bonds[aid1].keys())
        atomkeys2.sort()
        for aid2 in atomkeys2:
            if aid1 < aid2:
                atom1 = atom_dict[aid1]
                atom2 = atom_dict[aid2]
                order = bonds[aid1][aid2]
                if group:
                    bond = GroupBond(atom1, atom2, order)
                elif len(order) == 1:
                    bond = Bond(atom1, atom2, order[0])
                else:
                    raise InvalidAdjacencyListError(
                        'Error in adjacency list:\n{0}\nMultiple bond orders specified for '
                        'an atom in a Molecule.'.format(adjlist))
                atom1.edges[atom2] = bond
                atom2.edges[atom1] = bond

    if saturate_h:
        # Add explicit hydrogen atoms to complete structure if desired
        if not group:
            Saturator.saturate(atoms)

    # Consistency checks
    if not group:
        # Molecule consistency check
        # Electron and valency consistency check for each atom
        for atom in atoms:
            ConsistencyChecker.check_partial_charge(atom)

        n_rad = sum([atom.radical_electrons for atom in atoms])
        absolute_spin_per_electron = 1 / 2.
        if multiplicity is None:
            multiplicity = 2 * (n_rad * absolute_spin_per_electron) + 1

        ConsistencyChecker.check_multiplicity(n_rad, multiplicity)
        for atom in atoms:
            ConsistencyChecker.check_hund_rule(atom, multiplicity)
        return atoms, multiplicity
    else:
        # Currently no group consistency check
        return atoms, multiplicity
class TestGroupBond(unittest.TestCase):
    """
    Contains unit tests of the GroupBond class.
    """

    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.bond = GroupBond(None, None, order=["D"])
        self.orderList = [["S"], ["D"], ["T"], ["B"], ["S", "D"], ["D", "S"], ["D", "T"], ["S", "D", "T"]]

    def testApplyActionBreakBond(self):
        """
        Test the GroupBond.applyAction() method for a BREAK_BOND action.
        """
        action = ["BREAK_BOND", "*1", "S", "*2"]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail("GroupBond.applyAction() unexpectedly processed a BREAK_BOND action.")
            except ActionError:
                pass

    def testApplyActionFormBond(self):
        """
        Test the GroupBond.applyAction() method for a FORM_BOND action.
        """
        action = ["FORM_BOND", "*1", "S", "*2"]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail("GroupBond.applyAction() unexpectedly processed a FORM_BOND action.")
            except ActionError:
                pass

    def testApplyActionIncrementBond(self):
        """
        Test the GroupBond.applyAction() method for a CHANGE_BOND action.
        """
        action = ["CHANGE_BOND", "*1", 1, "*2"]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
            except ActionError:
                self.assertTrue("T" in order0 or "B" in order0)

    def testApplyActionDecrementBond(self):
        """
        Test the GroupBond.applyAction() method for a CHANGE_BOND action.
        """
        action = ["CHANGE_BOND", "*1", -1, "*2"]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
            except ActionError:
                self.assertTrue("S" in order0 or "B" in order0)

    def testApplyActionGainRadical(self):
        """
        Test the GroupBond.applyAction() method for a GAIN_RADICAL action.
        """
        action = ["GAIN_RADICAL", "*1", 1]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail("GroupBond.applyAction() unexpectedly processed a GAIN_RADICAL action.")
            except ActionError:
                pass

    def testApplyActionLoseRadical(self):
        """
        Test the GroupBond.applyAction() method for a LOSE_RADICAL action.
        """
        action = ["LOSE_RADICAL", "*1", 1]
        for order0 in self.orderList:
            bond0 = GroupBond(None, None, order=order0)
            bond = bond0.copy()
            try:
                bond.applyAction(action)
                self.fail("GroupBond.applyAction() unexpectedly processed a LOSE_RADICAL action.")
            except ActionError:
                pass

    def testEquivalent(self):
        """
        Test the GroupBond.equivalent() method.
        """
        for order1 in self.orderList:
            for order2 in self.orderList:
                bond1 = GroupBond(None, None, order=order1)
                bond2 = GroupBond(None, None, order=order2)
                if order1 == order2 or (all([o in order2 for o in order1]) and all([o in order1 for o in order2])):
                    self.assertTrue(bond1.equivalent(bond2))
                    self.assertTrue(bond2.equivalent(bond1))
                else:
                    self.assertFalse(bond1.equivalent(bond2))
                    self.assertFalse(bond2.equivalent(bond1))

    def testIsSpecificCaseOf(self):
        """
        Test the GroupBond.isSpecificCaseOf() method.
        """
        for order1 in self.orderList:
            for order2 in self.orderList:
                bond1 = GroupBond(None, None, order=order1)
                bond2 = GroupBond(None, None, order=order2)
                if order1 == order2 or all([o in order2 for o in order1]):
                    self.assertTrue(bond1.isSpecificCaseOf(bond2))
                else:
                    self.assertFalse(bond1.isSpecificCaseOf(bond2))

    def testCopy(self):
        """
        Test the GroupBond.copy() method.
        """
        bond = self.bond.copy()
        self.assertEqual(len(self.bond.order), len(bond.order))
        self.assertEqual(self.bond.order, bond.order)

    def testPickle(self):
        """
        Test that a GroupBond object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle

        bond = cPickle.loads(cPickle.dumps(self.bond))
        self.assertEqual(len(self.bond.order), len(bond.order))
        self.assertEqual(self.bond.order, bond.order)
Example #26
0
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.bond = GroupBond(None, None, order=['D'])
     self.orderList = [['S'], ['D'], ['T'], ['B'], ['S','D'], ['D','S'], ['D','T'], ['S','D','T']]