Exemplo n.º 1
0
 def testApplyActionLoseRadical(self):
     """
     Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
     """
     action = ['LOSE_RADICAL', '*1', 1]
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType],
                           radicalElectrons=[1],
                           charge=[0],
                           label='*1')
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType),
                              len(atomType.decrementRadical))
             for a in atomType.incrementRadical:
                 self.assertTrue(
                     a in atom.atomType,
                     "LOSE_RADICAL on {0} gave {1} not {2}".format(
                         atomType, atom.atomType,
                         atomType.decrementRadical))
             self.assertEqual(atom0.radicalElectrons,
                              [r + 1 for r in atom.radicalElectrons])
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
         except ActionError:
             self.assertEqual(len(atomType.decrementRadical), 0)
Exemplo n.º 2
0
    def testApplyActionLoseRadical(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1', lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(a in atom.atomType, "LOSE_RADICAL on {0} gave {1} not {2}".format(atomType, atom.atomType, atomType.decrementRadical))
                self.assertEqual(atom0.radicalElectrons, [r + 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.decrementRadical), 0)

        #test when radicals unspecified
        group = Group().fromAdjacencyList("""
        1 R ux
        """) #ux causes a wildcare for radicals
        atom1 = group.atoms[0]
        atom1.applyAction(action)
        self.assertListEqual(atom1.radicalElectrons, [0,1,2,3])
Exemplo n.º 3
0
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.atom = GroupAtom(atomType=[atomTypes['Cd']],
                           radicalElectrons=[1],
                           charge=[0],
                           label='*1')
 def testEquivalent(self):
     """
     Test the GroupAtom.equivalent() method.
     """
     for label1, atomType1 in atomTypes.iteritems():
         for label2, atomType2 in atomTypes.iteritems():
             atom1 = GroupAtom(atomType=[atomType1], radicalElectrons=[1], charge=[0], label="*1")
             atom2 = GroupAtom(atomType=[atomType2], radicalElectrons=[1], charge=[0], label="*1")
             if label1 == label2 or atomType2 in atomType1.generic or atomType1 in atomType2.generic:
                 self.assertTrue(atom1.equivalent(atom2), "{0!s} is not equivalent to {1!s}".format(atom1, atom2))
                 self.assertTrue(atom2.equivalent(atom1), "{0!s} is not equivalent to {1!s}".format(atom2, atom1))
             else:
                 self.assertFalse(atom1.equivalent(atom2), "{0!s} is equivalent to {1!s}".format(atom1, atom2))
                 self.assertFalse(atom2.equivalent(atom1), "{0!s} is equivalent to {1!s}".format(atom2, atom1))
         # Now see if charge and radical count are checked properly
         for charge in range(3):
             for radicals in range(2):
                 atom3 = GroupAtom(atomType=[atomType1], radicalElectrons=[radicals], charge=[charge], label="*1")
                 if radicals == 1 and charge == 0:
                     self.assertTrue(
                         atom1.equivalent(atom3), "{0!s} is not equivalent to {1!s}".format(atom1, atom3)
                     )
                     self.assertTrue(
                         atom1.equivalent(atom3), "{0!s} is not equivalent to {1!s}".format(atom3, atom1)
                     )
                 else:
                     self.assertFalse(atom1.equivalent(atom3), "{0!s} is equivalent to {1!s}".format(atom1, atom3))
                     self.assertFalse(atom1.equivalent(atom3), "{0!s} is equivalent to {1!s}".format(atom3, atom1))
 def testIsSpecificCaseOf(self):
     """
     Test the GroupAtom.isSpecificCaseOf() method.
     """
     for label1, atomType1 in atomTypes.iteritems():
         for label2, atomType2 in atomTypes.iteritems():
             atom1 = GroupAtom(atomType=[atomType1], radicalElectrons=[1], charge=[0], label="*1")
             atom2 = GroupAtom(atomType=[atomType2], radicalElectrons=[1], charge=[0], label="*1")
             # And make more generic types of these two atoms
             atom1gen = GroupAtom(atomType=[atomType1], radicalElectrons=[0, 1], charge=[0, 1], label="*1")
             atom2gen = GroupAtom(atomType=[atomType2], radicalElectrons=[0, 1], charge=[0, 1], label="*1")
             if label1 == label2 or atomType2 in atomType1.generic:
                 self.assertTrue(
                     atom1.isSpecificCaseOf(atom2), "{0!s} is not a specific case of {1!s}".format(atom1, atom2)
                 )
                 self.assertTrue(
                     atom1.isSpecificCaseOf(atom2gen),
                     "{0!s} is not a specific case of {1!s}".format(atom1, atom2gen),
                 )
                 self.assertFalse(
                     atom1gen.isSpecificCaseOf(atom2), "{0!s} is a specific case of {1!s}".format(atom1gen, atom2)
                 )
             else:
                 self.assertFalse(
                     atom1.isSpecificCaseOf(atom2), "{0!s} is a specific case of {1!s}".format(atom1, atom2)
                 )
                 self.assertFalse(
                     atom1.isSpecificCaseOf(atom2gen), "{0!s} is a specific case of {1!s}".format(atom1, atom2gen)
                 )
                 self.assertFalse(
                     atom1gen.isSpecificCaseOf(atom2), "{0!s} is a specific case of {1!s}".format(atom1gen, atom2)
                 )
Exemplo n.º 6
0
 def testApplyActionDecrementBond(self):
     """
     Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
     """
     action = ['CHANGE_BOND', '*1', -1, '*2']
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType), len(atomType.decrementBond))
             for a in atomType.decrementBond:
                 self.assertTrue(a in atom.atomType)
             self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
         except ActionError:
             self.assertEqual(len(atomType.decrementBond), 0)
Exemplo n.º 7
0
 def testApplyActionLoseRadical(self):
     """
     Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
     """
     action = ['LOSE_RADICAL', '*1', 1]
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType), len(atomType.decrementRadical))
             for a in atomType.incrementRadical:
                 self.assertTrue(a in atom.atomType, "LOSE_RADICAL on {0} gave {1} not {2}".format(atomType, atom.atomType, atomType.decrementRadical))
             self.assertEqual(atom0.radicalElectrons, [r + 1 for r in atom.radicalElectrons])
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
         except ActionError:
             self.assertEqual(len(atomType.decrementRadical), 0)
 def testApplyActionFormBond(self):
     """
     Test the GroupAtom.applyAction() method for a FORM_BOND action.
     """
     action = ["FORM_BOND", "*1", "S", "*2"]
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label="*1")
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType), len(atomType.formBond))
             for a in atomType.formBond:
                 self.assertTrue(a in atom.atomType)
             self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
         except ActionError:
             self.assertEqual(len(atomType.formBond), 0)
Exemplo n.º 9
0
    def testApplyActionLosePair(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_PAIR action when lonePairs is either specified or not.
        """

        action = ['LOSE_PAIR', '*1', 1]

        # lonePairs specified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1', lonePairs=[1])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementLonePair))
                for a in atomType.decrementLonePair:
                    self.assertTrue(a in atom.atomType,
                                    "LOSE_PAIR on {0} gave {1} not {2}".format(atomType, atom.atomType,
                                                                                  atomType.decrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, [r + 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.decrementLonePair), 0)

        #lonePairs unspecified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementLonePair))
                for a in atomType.decrementLonePair:
                    self.assertTrue(a in atom.atomType,
                                    "LOSE_PAIR on {0} gave {1} not {2}".format(atomType, atom.atomType,
                                                                               atomType.decrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual([1,2,3,4], [r + 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.decrementLonePair), 0)
Exemplo n.º 10
0
 def testApplyActionBreakBond(self):
     """
     Test the GroupAtom.applyAction() method for a BREAK_BOND action.
     """
     action = ['BREAK_BOND', '*1', 1, '*2']
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1', lonePairs=[0])
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType), len(atomType.breakBond))
             for a in atomType.breakBond:
                 self.assertTrue(a in atom.atomType)
             self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
             self.assertEqual(atom0.label, atom.label)
             self.assertEqual(atom0.lonePairs, atom.lonePairs)
         except ActionError:
             self.assertEqual(len(atomType.breakBond), 0)
Exemplo n.º 11
0
 def testApplyActionFormBond(self):
     """
     Test the GroupAtom.applyAction() method for a FORM_BOND action.
     """
     action = ['FORM_BOND', '*1', 'S', '*2']
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType],
                           radicalElectrons=[1],
                           charge=[0],
                           label='*1')
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType), len(atomType.formBond))
             for a in atomType.formBond:
                 self.assertTrue(a in atom.atomType)
             self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
         except ActionError:
             self.assertEqual(len(atomType.formBond), 0)
Exemplo n.º 12
0
    def testApplyActionLoseRadical(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_RADICAL on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementRadical))
                self.assertEqual(atom0.radicalElectrons,
                                 [r + 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.decrementRadical), 0)

        #test when radicals unspecified
        group = Group().fromAdjacencyList("""
        1 R ux
        """)  #ux causes a wildcare for radicals
        atom1 = group.atoms[0]
        atom1.applyAction(action)
        self.assertListEqual(atom1.radicalElectrons, [0, 1, 2, 3])
Exemplo n.º 13
0
 def testApplyActionIncrementBond(self):
     """
     Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
     """
     action = ['CHANGE_BOND', '*1', 1, '*2']
     for label, atomType in atomTypes.iteritems():
         atom0 = GroupAtom(atomType=[atomType],
                           radicalElectrons=[1],
                           charge=[0],
                           label='*1',
                           lonePairs=[0])
         atom = atom0.copy()
         try:
             atom.applyAction(action)
             self.assertEqual(len(atom.atomType),
                              len(atomType.incrementBond))
             for a in atomType.incrementBond:
                 self.assertTrue(a in atom.atomType)
             self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
             self.assertEqual(atom0.charge, atom.charge)
             self.assertEqual(atom0.label, atom.label)
             self.assertEqual(atom0.lonePairs, atom.lonePairs)
         except ActionError:
             self.assertEqual(len(atomType.incrementBond), 0)
Exemplo n.º 14
0
    def testApplyActionLosePair(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_PAIR action when lonePairs is either specified or not.
        """

        action = ['LOSE_PAIR', '*1', 1]

        # lonePairs specified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[1])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementLonePair))
                for a in atomType.decrementLonePair:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_PAIR on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs,
                                 [r + 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.decrementLonePair), 0)

        #lonePairs unspecified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementLonePair))
                for a in atomType.decrementLonePair:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_PAIR on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual([1, 2, 3, 4], [r + 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.decrementLonePair), 0)
Exemplo n.º 15
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
Exemplo n.º 16
0
 def testIsSpecificCaseOf(self):
     """
     Test the GroupAtom.isSpecificCaseOf() method.
     """
     for label1, atomType1 in atomTypes.iteritems():
         for label2, atomType2 in atomTypes.iteritems():
             atom1 = GroupAtom(atomType=[atomType1],
                               radicalElectrons=[1],
                               charge=[0],
                               label='*1',
                               lonePairs=[0])
             atom2 = GroupAtom(atomType=[atomType2],
                               radicalElectrons=[1],
                               charge=[0],
                               label='*1',
                               lonePairs=[0])
             # And make more generic types of these two atoms
             atom1gen = GroupAtom(atomType=[atomType1],
                                  radicalElectrons=[0, 1],
                                  charge=[0, 1],
                                  label='*1',
                                  lonePairs=[0, 1])
             atom2gen = GroupAtom(atomType=[atomType2],
                                  radicalElectrons=[0, 1],
                                  charge=[0, 1],
                                  label='*1',
                                  lonePairs=[0, 1])
             if label1 == label2 or atomType2 in atomType1.generic:
                 self.assertTrue(
                     atom1.isSpecificCaseOf(atom2),
                     '{0!s} is not a specific case of {1!s}'.format(
                         atom1, atom2))
                 self.assertTrue(
                     atom1.isSpecificCaseOf(atom2gen),
                     '{0!s} is not a specific case of {1!s}'.format(
                         atom1, atom2gen))
                 self.assertFalse(
                     atom1gen.isSpecificCaseOf(atom2),
                     '{0!s} is a specific case of {1!s}'.format(
                         atom1gen, atom2))
             else:
                 self.assertFalse(
                     atom1.isSpecificCaseOf(atom2),
                     '{0!s} is a specific case of {1!s}'.format(
                         atom1, atom2))
                 self.assertFalse(
                     atom1.isSpecificCaseOf(atom2gen),
                     '{0!s} is a specific case of {1!s}'.format(
                         atom1, atom2gen))
                 self.assertFalse(
                     atom1gen.isSpecificCaseOf(atom2),
                     '{0!s} is a specific case of {1!s}'.format(
                         atom1gen, atom2))
Exemplo n.º 17
0
 def testEquivalent(self):
     """
     Test the GroupAtom.equivalent() method.
     """
     for label1, atomType1 in atomTypes.iteritems():
         for label2, atomType2 in atomTypes.iteritems():
             atom1 = GroupAtom(atomType=[atomType1],
                               radicalElectrons=[1],
                               charge=[0],
                               label='*1',
                               lonePairs=[0])
             atom2 = GroupAtom(atomType=[atomType2],
                               radicalElectrons=[1],
                               charge=[0],
                               label='*1',
                               lonePairs=[0])
             if label1 == label2 or atomType2 in atomType1.generic or atomType1 in atomType2.generic:
                 self.assertTrue(
                     atom1.equivalent(atom2),
                     '{0!s} is not equivalent to {1!s}'.format(
                         atom1, atom2))
                 self.assertTrue(
                     atom2.equivalent(atom1),
                     '{0!s} is not equivalent to {1!s}'.format(
                         atom2, atom1))
             else:
                 self.assertFalse(
                     atom1.equivalent(atom2),
                     '{0!s} is equivalent to {1!s}'.format(atom1, atom2))
                 self.assertFalse(
                     atom2.equivalent(atom1),
                     '{0!s} is equivalent to {1!s}'.format(atom2, atom1))
         # Now see if charge and radical count are checked properly
         for charge in range(3):
             for radicals in range(2):
                 for lonePair in range(2):
                     atom3 = GroupAtom(atomType=[atomType1],
                                       radicalElectrons=[radicals],
                                       charge=[charge],
                                       label='*1',
                                       lonePairs=[lonePair])
                     if radicals == 1 and charge == 0 and lonePair == 0:
                         self.assertTrue(
                             atom1.equivalent(atom3),
                             '{0!s} is not equivalent to {1!s}'.format(
                                 atom1, atom3))
                         self.assertTrue(
                             atom1.equivalent(atom3),
                             '{0!s} is not equivalent to {1!s}'.format(
                                 atom3, atom1))
                     else:
                         self.assertFalse(
                             atom1.equivalent(atom3),
                             '{0!s} is equivalent to {1!s}'.format(
                                 atom1, atom3))
                         self.assertFalse(
                             atom1.equivalent(atom3),
                             '{0!s} is equivalent to {1!s}'.format(
                                 atom3, atom1))
Exemplo n.º 18
0
class TestGroupAtom(unittest.TestCase):
    """
    Contains unit tests of the GroupAtom class.
    """
    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.atom = GroupAtom(atomType=[atomTypes['Cd']],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])

    def testApplyActionBreakBond(self):
        """
        Test the GroupAtom.applyAction() method for a BREAK_BOND action.
        """
        action = ['BREAK_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.breakBond))
                for a in atomType.breakBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.breakBond), 0)

    def testApplyActionFormBond(self):
        """
        Test the GroupAtom.applyAction() method for a FORM_BOND action.
        """
        action = ['FORM_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.formBond))
                for a in atomType.formBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.formBond), 0)

    def testApplyActionIncrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.incrementBond))
                for a in atomType.incrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.incrementBond), 0)

    def testApplyActionDecrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', -1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementBond))
                for a in atomType.decrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.decrementBond), 0)

    def testApplyActionGainRadical(self):
        """
        Test the GroupAtom.applyAction() method for a GAIN_RADICAL action.
        """
        action = ['GAIN_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.incrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(
                        a in atom.atomType,
                        "GAIN_RADICAL on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.incrementRadical))
                self.assertEqual(atom0.radicalElectrons,
                                 [r - 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.incrementRadical), 0)

        #test when radicals unspecified
        group = Group().fromAdjacencyList("""
        1 R ux
        """)  #ux causes a wildcare for radicals
        atom1 = group.atoms[0]
        atom1.applyAction(action)
        self.assertListEqual(atom1.radicalElectrons, [1, 2, 3, 4])

    def testApplyActionLoseRadical(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_RADICAL on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementRadical))
                self.assertEqual(atom0.radicalElectrons,
                                 [r + 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs, atom.lonePairs)
            except ActionError:
                self.assertEqual(len(atomType.decrementRadical), 0)

        #test when radicals unspecified
        group = Group().fromAdjacencyList("""
        1 R ux
        """)  #ux causes a wildcare for radicals
        atom1 = group.atoms[0]
        atom1.applyAction(action)
        self.assertListEqual(atom1.radicalElectrons, [0, 1, 2, 3])

    def testApplyActionGainPair(self):
        """
        Test the GroupAtom.applyAction() method for a GAIN_PAIR action when lonePairs is either specified or not.
        """

        action = ['GAIN_PAIR', '*1', 1]

        #lonePairs specified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[0])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.incrementLonePair))
                for a in atomType.incrementLonePair:
                    self.assertTrue(
                        a in atom.atomType,
                        "GAIN_PAIR on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.incrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs,
                                 [r - 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.incrementLonePair), 0)

        #lonePairs unspecified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.incrementLonePair))
                for a in atomType.incrementLonePair:
                    self.assertTrue(
                        a in atom.atomType,
                        "GAIN_PAIR on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.incrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual([0, 1, 2, 3], [r - 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.incrementLonePair), 0)

    def testApplyActionLosePair(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_PAIR action when lonePairs is either specified or not.
        """

        action = ['LOSE_PAIR', '*1', 1]

        # lonePairs specified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1',
                              lonePairs=[1])
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementLonePair))
                for a in atomType.decrementLonePair:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_PAIR on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual(atom0.lonePairs,
                                 [r + 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.decrementLonePair), 0)

        #lonePairs unspecified:
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementLonePair))
                for a in atomType.decrementLonePair:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_PAIR on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementLonePair))
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
                self.assertEqual([1, 2, 3, 4], [r + 1 for r in atom.lonePairs])
            except ActionError:
                self.assertEqual(len(atomType.decrementLonePair), 0)

    def testEquivalent(self):
        """
        Test the GroupAtom.equivalent() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1',
                                  lonePairs=[0])
                atom2 = GroupAtom(atomType=[atomType2],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1',
                                  lonePairs=[0])
                if label1 == label2 or atomType2 in atomType1.generic or atomType1 in atomType2.generic:
                    self.assertTrue(
                        atom1.equivalent(atom2),
                        '{0!s} is not equivalent to {1!s}'.format(
                            atom1, atom2))
                    self.assertTrue(
                        atom2.equivalent(atom1),
                        '{0!s} is not equivalent to {1!s}'.format(
                            atom2, atom1))
                else:
                    self.assertFalse(
                        atom1.equivalent(atom2),
                        '{0!s} is equivalent to {1!s}'.format(atom1, atom2))
                    self.assertFalse(
                        atom2.equivalent(atom1),
                        '{0!s} is equivalent to {1!s}'.format(atom2, atom1))
            # Now see if charge and radical count are checked properly
            for charge in range(3):
                for radicals in range(2):
                    for lonePair in range(2):
                        atom3 = GroupAtom(atomType=[atomType1],
                                          radicalElectrons=[radicals],
                                          charge=[charge],
                                          label='*1',
                                          lonePairs=[lonePair])
                        if radicals == 1 and charge == 0 and lonePair == 0:
                            self.assertTrue(
                                atom1.equivalent(atom3),
                                '{0!s} is not equivalent to {1!s}'.format(
                                    atom1, atom3))
                            self.assertTrue(
                                atom1.equivalent(atom3),
                                '{0!s} is not equivalent to {1!s}'.format(
                                    atom3, atom1))
                        else:
                            self.assertFalse(
                                atom1.equivalent(atom3),
                                '{0!s} is equivalent to {1!s}'.format(
                                    atom1, atom3))
                            self.assertFalse(
                                atom1.equivalent(atom3),
                                '{0!s} is equivalent to {1!s}'.format(
                                    atom3, atom1))

    def testIsSpecificCaseOf(self):
        """
        Test the GroupAtom.isSpecificCaseOf() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1',
                                  lonePairs=[0])
                atom2 = GroupAtom(atomType=[atomType2],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1',
                                  lonePairs=[0])
                # And make more generic types of these two atoms
                atom1gen = GroupAtom(atomType=[atomType1],
                                     radicalElectrons=[0, 1],
                                     charge=[0, 1],
                                     label='*1',
                                     lonePairs=[0, 1])
                atom2gen = GroupAtom(atomType=[atomType2],
                                     radicalElectrons=[0, 1],
                                     charge=[0, 1],
                                     label='*1',
                                     lonePairs=[0, 1])
                if label1 == label2 or atomType2 in atomType1.generic:
                    self.assertTrue(
                        atom1.isSpecificCaseOf(atom2),
                        '{0!s} is not a specific case of {1!s}'.format(
                            atom1, atom2))
                    self.assertTrue(
                        atom1.isSpecificCaseOf(atom2gen),
                        '{0!s} is not a specific case of {1!s}'.format(
                            atom1, atom2gen))
                    self.assertFalse(
                        atom1gen.isSpecificCaseOf(atom2),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1gen, atom2))
                else:
                    self.assertFalse(
                        atom1.isSpecificCaseOf(atom2),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1, atom2))
                    self.assertFalse(
                        atom1.isSpecificCaseOf(atom2gen),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1, atom2gen))
                    self.assertFalse(
                        atom1gen.isSpecificCaseOf(atom2),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1gen, atom2))

    def testCopy(self):
        """
        Test the GroupAtom.copy() method.
        """
        atom = self.atom.copy()
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)
        self.assertEqual(self.atom.lonePairs, atom.lonePairs)

    def testPickle(self):
        """
        Test that a GroupAtom object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle
        atom = cPickle.loads(cPickle.dumps(self.atom))
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)
        self.assertEqual(self.atom.lonePairs, atom.lonePairs)

    def testHasWildcards(self):
        """
        Tests the GroupAtom.hasWildcards() method
        """
        self.assertFalse(self.atom.hasWildcards())
        adjlist = """
1 *2 C     u0     {2,[D,T]} {3,S}
2 *3 C     u0     {1,[D,T]} {4,S}
3    C     ux     {1,S} {5,S}
4    C     u[0,1] {2,S}
5    [C,O] u0     {3,S}
"""
        group = Group().fromAdjacencyList(adjlist)
        for index, atom in enumerate(group.atoms):
            self.assertTrue(
                atom.hasWildcards(),
                'GroupAtom with index {0} should have wildcards, but does not'.
                format(index))

    def testMakeSampleAtom(self):
        """
        Tests the GroupAtom.makeSampleAtom() method
        """
        newAtom = self.atom.makeSampleAtom()

        self.assertEquals(newAtom.element, elements.__dict__['C'])
        self.assertEquals(newAtom.radicalElectrons, 1)
        self.assertEquals(newAtom.charge, 0)
        self.assertEquals(newAtom.lonePairs, 0)
Exemplo n.º 19
0
class TestGroupAtom(unittest.TestCase):
    """
    Contains unit tests of the GroupAtom class.
    """

    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.atom = GroupAtom(atomType=[atomTypes['Cd']], radicalElectrons=[1], charge=[0], label='*1')
    
    def testApplyActionBreakBond(self):
        """
        Test the GroupAtom.applyAction() method for a BREAK_BOND action.
        """
        action = ['BREAK_BOND', '*1', 'S', '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.breakBond))
                for a in atomType.breakBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.breakBond), 0)
    
    def testApplyActionFormBond(self):
        """
        Test the GroupAtom.applyAction() method for a FORM_BOND action.
        """
        action = ['FORM_BOND', '*1', 'S', '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.formBond))
                for a in atomType.formBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.formBond), 0)
    
    def testApplyActionIncrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.incrementBond))
                for a in atomType.incrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.incrementBond), 0)
    
    def testApplyActionDecrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', -1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementBond))
                for a in atomType.decrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.decrementBond), 0)
    
    def testApplyActionGainRadical(self):
        """
        Test the GroupAtom.applyAction() method for a GAIN_RADICAL action.
        """
        action = ['GAIN_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.incrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(a in atom.atomType, "GAIN_RADICAL on {0} gave {1} not {2}".format(atomType, atom.atomType, atomType.incrementRadical))
                self.assertEqual(atom0.radicalElectrons, [r - 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.incrementRadical), 0)
    
    def testApplyActionLoseRadical(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(a in atom.atomType, "LOSE_RADICAL on {0} gave {1} not {2}".format(atomType, atom.atomType, atomType.decrementRadical))
                self.assertEqual(atom0.radicalElectrons, [r + 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.decrementRadical), 0)
    
    def testEquivalent(self):
        """
        Test the GroupAtom.equivalent() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1], radicalElectrons=[1], charge=[0], label='*1')
                atom2 = GroupAtom(atomType=[atomType2], radicalElectrons=[1], charge=[0], label='*1')
                if label1 == label2 or atomType2 in atomType1.generic or atomType1 in atomType2.generic:
                    self.assertTrue(atom1.equivalent(atom2), '{0!s} is not equivalent to {1!s}'.format(atom1, atom2))
                    self.assertTrue(atom2.equivalent(atom1), '{0!s} is not equivalent to {1!s}'.format(atom2, atom1))
                else:
                    self.assertFalse(atom1.equivalent(atom2), '{0!s} is equivalent to {1!s}'.format(atom1, atom2))
                    self.assertFalse(atom2.equivalent(atom1), '{0!s} is equivalent to {1!s}'.format(atom2, atom1))
            # Now see if charge and radical count are checked properly
            for charge in range(3):
                for radicals in range(2):
                    atom3 = GroupAtom(atomType=[atomType1], radicalElectrons=[radicals], charge=[charge], label='*1')
                    if radicals == 1 and charge == 0:
                        self.assertTrue(atom1.equivalent(atom3), '{0!s} is not equivalent to {1!s}'.format(atom1, atom3))
                        self.assertTrue(atom1.equivalent(atom3), '{0!s} is not equivalent to {1!s}'.format(atom3, atom1))
                    else:
                        self.assertFalse(atom1.equivalent(atom3), '{0!s} is equivalent to {1!s}'.format(atom1, atom3))
                        self.assertFalse(atom1.equivalent(atom3), '{0!s} is equivalent to {1!s}'.format(atom3, atom1))

    def testIsSpecificCaseOf(self):
        """
        Test the GroupAtom.isSpecificCaseOf() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1], radicalElectrons=[1], charge=[0], label='*1')
                atom2 = GroupAtom(atomType=[atomType2], radicalElectrons=[1], charge=[0], label='*1')
                # And make more generic types of these two atoms
                atom1gen = GroupAtom(atomType=[atomType1], radicalElectrons=[0, 1], charge=[0, 1], label='*1')
                atom2gen = GroupAtom(atomType=[atomType2], radicalElectrons=[0, 1], charge=[0, 1], label='*1')
                if label1 == label2 or atomType2 in atomType1.generic:
                    self.assertTrue(atom1.isSpecificCaseOf(atom2), '{0!s} is not a specific case of {1!s}'.format(atom1, atom2))
                    self.assertTrue(atom1.isSpecificCaseOf(atom2gen), '{0!s} is not a specific case of {1!s}'.format(atom1, atom2gen))
                    self.assertFalse(atom1gen.isSpecificCaseOf(atom2), '{0!s} is a specific case of {1!s}'.format(atom1gen, atom2))
                else:
                    self.assertFalse(atom1.isSpecificCaseOf(atom2), '{0!s} is a specific case of {1!s}'.format(atom1, atom2))
                    self.assertFalse(atom1.isSpecificCaseOf(atom2gen), '{0!s} is a specific case of {1!s}'.format(atom1, atom2gen))
                    self.assertFalse(atom1gen.isSpecificCaseOf(atom2), '{0!s} is a specific case of {1!s}'.format(atom1gen, atom2))
    
    def testCopy(self):
        """
        Test the GroupAtom.copy() method.
        """
        atom = self.atom.copy()
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)
    
    def testPickle(self):
        """
        Test that a GroupAtom object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle
        atom = cPickle.loads(cPickle.dumps(self.atom))
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)
Exemplo n.º 20
0
class TestGroupAtom(unittest.TestCase):
    """
    Contains unit tests of the GroupAtom class.
    """

    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.atom = GroupAtom(atomType=[atomTypes['Cd']], radicalElectrons=[1], charge=[0], label='*1')
    
    def testApplyActionBreakBond(self):
        """
        Test the GroupAtom.applyAction() method for a BREAK_BOND action.
        """
        action = ['BREAK_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.breakBond))
                for a in atomType.breakBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.breakBond), 0)
    
    def testApplyActionFormBond(self):
        """
        Test the GroupAtom.applyAction() method for a FORM_BOND action.
        """
        action = ['FORM_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.formBond))
                for a in atomType.formBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.formBond), 0)
    
    def testApplyActionIncrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.incrementBond))
                for a in atomType.incrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.incrementBond), 0)
    
    def testApplyActionDecrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', -1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementBond))
                for a in atomType.decrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.decrementBond), 0)
    
    def testApplyActionGainRadical(self):
        """
        Test the GroupAtom.applyAction() method for a GAIN_RADICAL action.
        """
        action = ['GAIN_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.incrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(a in atom.atomType, "GAIN_RADICAL on {0} gave {1} not {2}".format(atomType, atom.atomType, atomType.incrementRadical))
                self.assertEqual(atom0.radicalElectrons, [r - 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.incrementRadical), 0)
    
    def testApplyActionLoseRadical(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType], radicalElectrons=[1], charge=[0], label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.decrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(a in atom.atomType, "LOSE_RADICAL on {0} gave {1} not {2}".format(atomType, atom.atomType, atomType.decrementRadical))
                self.assertEqual(atom0.radicalElectrons, [r + 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.decrementRadical), 0)
    
    def testEquivalent(self):
        """
        Test the GroupAtom.equivalent() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1], radicalElectrons=[1], charge=[0], label='*1')
                atom2 = GroupAtom(atomType=[atomType2], radicalElectrons=[1], charge=[0], label='*1')
                if label1 == label2 or atomType2 in atomType1.generic or atomType1 in atomType2.generic:
                    self.assertTrue(atom1.equivalent(atom2), '{0!s} is not equivalent to {1!s}'.format(atom1, atom2))
                    self.assertTrue(atom2.equivalent(atom1), '{0!s} is not equivalent to {1!s}'.format(atom2, atom1))
                else:
                    self.assertFalse(atom1.equivalent(atom2), '{0!s} is equivalent to {1!s}'.format(atom1, atom2))
                    self.assertFalse(atom2.equivalent(atom1), '{0!s} is equivalent to {1!s}'.format(atom2, atom1))
            # Now see if charge and radical count are checked properly
            for charge in range(3):
                for radicals in range(2):
                    atom3 = GroupAtom(atomType=[atomType1], radicalElectrons=[radicals], charge=[charge], label='*1')
                    if radicals == 1 and charge == 0:
                        self.assertTrue(atom1.equivalent(atom3), '{0!s} is not equivalent to {1!s}'.format(atom1, atom3))
                        self.assertTrue(atom1.equivalent(atom3), '{0!s} is not equivalent to {1!s}'.format(atom3, atom1))
                    else:
                        self.assertFalse(atom1.equivalent(atom3), '{0!s} is equivalent to {1!s}'.format(atom1, atom3))
                        self.assertFalse(atom1.equivalent(atom3), '{0!s} is equivalent to {1!s}'.format(atom3, atom1))

    def testIsSpecificCaseOf(self):
        """
        Test the GroupAtom.isSpecificCaseOf() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1], radicalElectrons=[1], charge=[0], label='*1')
                atom2 = GroupAtom(atomType=[atomType2], radicalElectrons=[1], charge=[0], label='*1')
                # And make more generic types of these two atoms
                atom1gen = GroupAtom(atomType=[atomType1], radicalElectrons=[0, 1], charge=[0, 1], label='*1')
                atom2gen = GroupAtom(atomType=[atomType2], radicalElectrons=[0, 1], charge=[0, 1], label='*1')
                if label1 == label2 or atomType2 in atomType1.generic:
                    self.assertTrue(atom1.isSpecificCaseOf(atom2), '{0!s} is not a specific case of {1!s}'.format(atom1, atom2))
                    self.assertTrue(atom1.isSpecificCaseOf(atom2gen), '{0!s} is not a specific case of {1!s}'.format(atom1, atom2gen))
                    self.assertFalse(atom1gen.isSpecificCaseOf(atom2), '{0!s} is a specific case of {1!s}'.format(atom1gen, atom2))
                else:
                    self.assertFalse(atom1.isSpecificCaseOf(atom2), '{0!s} is a specific case of {1!s}'.format(atom1, atom2))
                    self.assertFalse(atom1.isSpecificCaseOf(atom2gen), '{0!s} is a specific case of {1!s}'.format(atom1, atom2gen))
                    self.assertFalse(atom1gen.isSpecificCaseOf(atom2), '{0!s} is a specific case of {1!s}'.format(atom1gen, atom2))
    
    def testCopy(self):
        """
        Test the GroupAtom.copy() method.
        """
        atom = self.atom.copy()
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)
    
    def testPickle(self):
        """
        Test that a GroupAtom object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle
        atom = cPickle.loads(cPickle.dumps(self.atom))
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)

    def testHasWildcards(self):
        """
        Tests the GroupAtom.hasWildcards() method
        """
        self.assertFalse(self.atom.hasWildcards())
        adjlist = """
1 *2 C     u0     {2,[D,T]} {3,S}
2 *3 C     u0     {1,[D,T]} {4,S}
3    C     ux     {1,S} {5,S}
4    C     u[0,1] {2,S}
5    [C,O] u0     {3,S}
"""
        group = Group().fromAdjacencyList(adjlist)
        for index, atom in enumerate(group.atoms):
            self.assertTrue(atom.hasWildcards(), 'GroupAtom with index {0} should have wildcards, but does not'.format(index))

    def testMakeSampleAtom(self):
        """
        Tests the GroupAtom.makeSampleAtom() method
        """
        newAtom = self.atom.makeSampleAtom()

        self.assertEquals(newAtom.element, elements.__dict__['C'])
        self.assertEquals(newAtom.radicalElectrons, 1)
        self.assertEquals(newAtom.charge, 0)
Exemplo n.º 21
0
 def setUp(self):
     """
     A method called before each unit test in this class.
     """
     self.atom = GroupAtom(atomType=[atomTypes['Cd']], radicalElectrons=[1], charge=[0], label='*1')
Exemplo n.º 22
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
Exemplo n.º 23
0
class TestGroupAtom(unittest.TestCase):
    """
    Contains unit tests of the GroupAtom class.
    """
    def setUp(self):
        """
        A method called before each unit test in this class.
        """
        self.atom = GroupAtom(atomType=[atomTypes['Cd']],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')

    def testApplyActionBreakBond(self):
        """
        Test the GroupAtom.applyAction() method for a BREAK_BOND action.
        """
        action = ['BREAK_BOND', '*1', 'S', '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.breakBond))
                for a in atomType.breakBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.breakBond), 0)

    def testApplyActionFormBond(self):
        """
        Test the GroupAtom.applyAction() method for a FORM_BOND action.
        """
        action = ['FORM_BOND', '*1', 'S', '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType), len(atomType.formBond))
                for a in atomType.formBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.formBond), 0)

    def testApplyActionIncrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', 1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.incrementBond))
                for a in atomType.incrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.incrementBond), 0)

    def testApplyActionDecrementBond(self):
        """
        Test the GroupAtom.applyAction() method for a CHANGE_BOND action.
        """
        action = ['CHANGE_BOND', '*1', -1, '*2']
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementBond))
                for a in atomType.decrementBond:
                    self.assertTrue(a in atom.atomType)
                self.assertEqual(atom0.radicalElectrons, atom.radicalElectrons)
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.decrementBond), 0)

    def testApplyActionGainRadical(self):
        """
        Test the GroupAtom.applyAction() method for a GAIN_RADICAL action.
        """
        action = ['GAIN_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.incrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(
                        a in atom.atomType,
                        "GAIN_RADICAL on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.incrementRadical))
                self.assertEqual(atom0.radicalElectrons,
                                 [r - 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.incrementRadical), 0)

    def testApplyActionLoseRadical(self):
        """
        Test the GroupAtom.applyAction() method for a LOSE_RADICAL action.
        """
        action = ['LOSE_RADICAL', '*1', 1]
        for label, atomType in atomTypes.iteritems():
            atom0 = GroupAtom(atomType=[atomType],
                              radicalElectrons=[1],
                              charge=[0],
                              label='*1')
            atom = atom0.copy()
            try:
                atom.applyAction(action)
                self.assertEqual(len(atom.atomType),
                                 len(atomType.decrementRadical))
                for a in atomType.incrementRadical:
                    self.assertTrue(
                        a in atom.atomType,
                        "LOSE_RADICAL on {0} gave {1} not {2}".format(
                            atomType, atom.atomType,
                            atomType.decrementRadical))
                self.assertEqual(atom0.radicalElectrons,
                                 [r + 1 for r in atom.radicalElectrons])
                self.assertEqual(atom0.charge, atom.charge)
                self.assertEqual(atom0.label, atom.label)
            except ActionError:
                self.assertEqual(len(atomType.decrementRadical), 0)

    def testEquivalent(self):
        """
        Test the GroupAtom.equivalent() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1')
                atom2 = GroupAtom(atomType=[atomType2],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1')
                if label1 == label2 or atomType2 in atomType1.generic or atomType1 in atomType2.generic:
                    self.assertTrue(
                        atom1.equivalent(atom2),
                        '{0!s} is not equivalent to {1!s}'.format(
                            atom1, atom2))
                    self.assertTrue(
                        atom2.equivalent(atom1),
                        '{0!s} is not equivalent to {1!s}'.format(
                            atom2, atom1))
                else:
                    self.assertFalse(
                        atom1.equivalent(atom2),
                        '{0!s} is equivalent to {1!s}'.format(atom1, atom2))
                    self.assertFalse(
                        atom2.equivalent(atom1),
                        '{0!s} is equivalent to {1!s}'.format(atom2, atom1))
            # Now see if charge and radical count are checked properly
            for charge in range(3):
                for radicals in range(2):
                    atom3 = GroupAtom(atomType=[atomType1],
                                      radicalElectrons=[radicals],
                                      charge=[charge],
                                      label='*1')
                    if radicals == 1 and charge == 0:
                        self.assertTrue(
                            atom1.equivalent(atom3),
                            '{0!s} is not equivalent to {1!s}'.format(
                                atom1, atom3))
                        self.assertTrue(
                            atom1.equivalent(atom3),
                            '{0!s} is not equivalent to {1!s}'.format(
                                atom3, atom1))
                    else:
                        self.assertFalse(
                            atom1.equivalent(atom3),
                            '{0!s} is equivalent to {1!s}'.format(
                                atom1, atom3))
                        self.assertFalse(
                            atom1.equivalent(atom3),
                            '{0!s} is equivalent to {1!s}'.format(
                                atom3, atom1))

    def testIsSpecificCaseOf(self):
        """
        Test the GroupAtom.isSpecificCaseOf() method.
        """
        for label1, atomType1 in atomTypes.iteritems():
            for label2, atomType2 in atomTypes.iteritems():
                atom1 = GroupAtom(atomType=[atomType1],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1')
                atom2 = GroupAtom(atomType=[atomType2],
                                  radicalElectrons=[1],
                                  charge=[0],
                                  label='*1')
                # And make more generic types of these two atoms
                atom1gen = GroupAtom(atomType=[atomType1],
                                     radicalElectrons=[0, 1],
                                     charge=[0, 1],
                                     label='*1')
                atom2gen = GroupAtom(atomType=[atomType2],
                                     radicalElectrons=[0, 1],
                                     charge=[0, 1],
                                     label='*1')
                if label1 == label2 or atomType2 in atomType1.generic:
                    self.assertTrue(
                        atom1.isSpecificCaseOf(atom2),
                        '{0!s} is not a specific case of {1!s}'.format(
                            atom1, atom2))
                    self.assertTrue(
                        atom1.isSpecificCaseOf(atom2gen),
                        '{0!s} is not a specific case of {1!s}'.format(
                            atom1, atom2gen))
                    self.assertFalse(
                        atom1gen.isSpecificCaseOf(atom2),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1gen, atom2))
                else:
                    self.assertFalse(
                        atom1.isSpecificCaseOf(atom2),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1, atom2))
                    self.assertFalse(
                        atom1.isSpecificCaseOf(atom2gen),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1, atom2gen))
                    self.assertFalse(
                        atom1gen.isSpecificCaseOf(atom2),
                        '{0!s} is a specific case of {1!s}'.format(
                            atom1gen, atom2))

    def testCopy(self):
        """
        Test the GroupAtom.copy() method.
        """
        atom = self.atom.copy()
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)

    def testPickle(self):
        """
        Test that a GroupAtom object can be successfully pickled and
        unpickled with no loss of information.
        """
        import cPickle
        atom = cPickle.loads(cPickle.dumps(self.atom))
        self.assertEqual(len(self.atom.atomType), len(atom.atomType))
        self.assertEqual(self.atom.atomType[0].label, atom.atomType[0].label)
        self.assertEqual(self.atom.radicalElectrons, atom.radicalElectrons)
        self.assertEqual(self.atom.charge, atom.charge)
        self.assertEqual(self.atom.label, atom.label)
2 *2 R!H u0 r1 {1,[D,T]}
""")

    # Calculate number of atoms to add
    if d0:  # long
        a = s0 + r0 - p0 - 1  # Add to main chain
        b = p0 - 1  # To close other side of ring
    else:  # short
        a = s0 + p0 - 1  # Add to main chain
        b = r0 - p0 - 1  # To close other side of ring

    # Add on the side chain
    if a > 0:
        # Create atoms
        atoms = [
            GroupAtom(atomType=['R!H'], radicalElectrons=None, label='')
            for i in range(a)
        ]
        # Set radical num to 1 for first atom
        atoms[0].radicalElectrons = [1]
        # Set ring properties for all atoms
        if s0 == 0:
            for atom in atoms:
                atom.props['inRing'] = True
        elif s0 > 0:
            for atom in atoms[0:s0 - 1]:
                atom.props['inRing'] = False
            for atom in atoms[s0:]:
                atom.props['inRing'] = True
        # Label atoms
        atoms[0].label = '*1'