Skip to content

Commit

Permalink
Triplet S2 perception (#778)
Browse files Browse the repository at this point in the history
Fixes an issue with perceiving S2 as a triplet from xyz, building upon #756
  • Loading branch information
alongd authored Dec 8, 2024
2 parents 84898ee + 92bbc31 commit 6fda63e
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 13 deletions.
14 changes: 8 additions & 6 deletions arc/species/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1380,12 +1380,14 @@ def molecules_from_xyz(xyz: Optional[Union[dict, str]],
return None, None
xyz = check_xyz_dict(xyz)

if xyz['symbols'] == ('O', 'O') and multiplicity != 1:
coords = np.asarray(xyz['coords'], dtype=np.float32)
vector = coords[0] - coords[1]
if float(np.dot(vector, vector) ** 0.5) < 1.4:
# Special case for O2 triplet
return Molecule(smiles='[O][O]'), Molecule(smiles='[O][O]')
if len(xyz['symbols']) == 2:
for element, bond_length in zip(['O', 'S'], [1.4, 2.1]):
if xyz['symbols'] == (element, element) and multiplicity != 1:
coords = np.asarray(xyz['coords'], dtype=np.float32)
vector = coords[0] - coords[1]
if float(np.dot(vector, vector) ** 0.5) < bond_length:
# Special case for O2 and S2 triplet
return Molecule(smiles=f'[{element}][{element}]'), Molecule(smiles=f'[{element}][{element}]')

# 1. Generate a molecule with no bond order information with atoms ordered as in xyz.
mol_graph = MolGraph(symbols=xyz['symbols'], coords=xyz['coords'])
Expand Down
8 changes: 6 additions & 2 deletions arc/species/converter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3588,8 +3588,10 @@ def test_xyz_to_smiles(self):
mol18 = converter.molecules_from_xyz(converter.str_to_xyz(xyz18))[1]
mol19 = converter.molecules_from_xyz(converter.str_to_xyz(xyz19))[1]
mol20 = converter.molecules_from_xyz(converter.str_to_xyz(xyz20))[1]
mol21 = converter.molecules_from_xyz("""O 0.0000000 0.0000000 0.6076340
O 0.0000000 0.0000000 -0.6076340""")[1]
mol21 = converter.molecules_from_xyz("""O 0.0000000 0.0000000 0.6076340
O 0.0000000 0.0000000 -0.6076340""")[1]
mol22 = converter.molecules_from_xyz("""S 0.0000000 0.0000000 0.9547220
S 0.0000000 0.0000000 -0.9547220""")[1]

self.assertEqual(mol1.to_smiles(), '[NH-][S+](=O)(O)C')
self.assertIn(mol2.to_smiles(), ['COC1=C(CO)C=C([C](C)C)C=C1', 'COC1C=CC(=CC=1CO)[C](C)C'])
Expand Down Expand Up @@ -3622,6 +3624,8 @@ def test_xyz_to_smiles(self):
self.assertEqual(mol20.to_smiles(), 'C=C[CH]C=CC')
self.assertEqual(mol21.to_smiles(), '[O][O]')
self.assertEqual(mol21.multiplicity, 3)
self.assertEqual(mol22.to_smiles(), '[S][S]')
self.assertEqual(mol22.multiplicity, 3)

def test_to_rdkit_mol(self):
"""Test converting an RMG Molecule object to an RDKit Molecule object"""
Expand Down
5 changes: 0 additions & 5 deletions arc/species/species.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,11 +1583,6 @@ def mol_from_xyz(self,
if xyz is None:
return None

if len(xyz['symbols']) == 2 and xyz['symbols'][0] == xyz['symbols'][1] \
and xyz['symbols'][0] in ['O', 'S'] and self.multiplicity == 3:
# Hard-coded for triplet O2 and S2: Don't perceive mol.
return None

if self.mol is not None:
if len(self.mol.atoms) != len(xyz['symbols']):
raise SpeciesError(f'The number of atoms in the molecule and in the coordinates of {self.label} is different.'
Expand Down
18 changes: 18 additions & 0 deletions arc/species/species_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,24 @@ def test_mol_from_xyz(self):
so2.mol_from_xyz(xyz=so2_t_xyz, get_cheap=False)
self.assertEqual([atom.element.symbol for atom in so2.mol.atoms], ['S', 'O', 'O'])

# S2
s2_xyz = """S 0.0000000 0.0000000 0.9547220
S 0.0000000 0.0000000 -0.9547220"""
s2 = ARCSpecies(label='S2', smiles='[S][S]')
self.assertEqual(s2.multiplicity, 3)
self.assertEqual(s2.charge, 0)
s2.mol_from_xyz(xyz=str_to_xyz(s2_xyz), get_cheap=False)
self.assertEqual([atom.element.symbol for atom in s2.mol.atoms], ['S', 'S'])
self.assertEqual(s2.mol.to_smiles(), '[S][S]')
self.assertEqual(s2.mol.multiplicity, 3)

# O2
o2_xyz = {'symbols': ('O', 'O'), 'isotopes': (16, 16), 'coords': ((0.0, 0.0, 0.6029), (0.0, 0.0, -0.6029))}
o2 = ARCSpecies(label='O2aa', xyz=o2_xyz, multiplicity=3)
self.assertEqual(o2.multiplicity, 3)
self.assertEqual(o2.mol.multiplicity, 3)
self.assertEqual(o2.mol.to_smiles(), '[O][O]')

def test_consistent_atom_order(self):
"""Test that the atom order is preserved whether starting from SMILES or from xyz"""
xyz9 = """O -1.17310019 -0.30822930 0.16269772
Expand Down

0 comments on commit 6fda63e

Please sign in to comment.