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 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 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) )
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 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)
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 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', '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 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 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 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 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
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 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))
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)
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)
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)
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
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'