Repository: duartegroup/autodE

Issue:
Wrong Multiplicity
**Describe the bug**
SMILES parser generates the wrong Multiplicity

**To Reproduce**
import autode as ade

ade.Config.n_cores = 28
ade.Config.hcode = 'g16'


ade.Config.G16.keywords.set_opt_basis_set('6-31+G(d)')
ade.Config.G16.keywords.sp.basis_set = '6-311+G(d,p)'
ade.Config.G16.keywords.set_opt_functional('B3LYP')
ade.Config.G16.keywords.set_functional('B3LYP')

test = ade.Molecule(name='test',smiles='CCC1=CC=C(C=C1)C[Na]')
test.print_xyz_file()


**Expected behavior**
The expected results are as follows
 INFO     Initialisation with SMILES successful. Charge=0, Multiplicity=1, Num. Atoms=21

**Environment**
 - OS: Unix
 - Version: 1.0.3

**Additional context**

INFO     Generating a Molecule object for test
INFO     Parsing CCC1=CC=C(C=C1)C[Na]
INFO     Inverting stereochemistry
INFO     Parsed SMILES in: 0.42 ms
INFO     Setting 21 atom types
INFO     Have 1 ring(s)
INFO     Queue: [1]
INFO     Queue: [2]
INFO     Queue: [7, 3]
INFO     Queue: [3, 6]
INFO     Queuing Dihedral(idxs=[1, 2, 3, 4], φ0=3.14)
INFO     Have 1 dihedral(s) to rotate
INFO     Resetting sites on atom 4
INFO     Rotating 3 sites onto 1 points
INFO     Resetting sites on atom 6
INFO     Rotating 3 sites onto 1 points
INFO     Queue: [6, 4]
INFO     Queuing Dihedral(idxs=[5, 6, 7, 2], φ0=0)
INFO     Have 1 dihedral(s) to rotate
WARNING  Could not apply rotation Dihedral(idxs=[5, 6, 7, 2], φ0=0)
INFO     Queue: [4, 5]
INFO     Closing ring on: SMILESBond([4, 5], order=2) and adjusting atoms
INFO     Adjusting ring dihedrals to close the ring
INFO     Closed ring in: 1.66 ms
INFO     A ring was poorly closed - adjusting angles
WARNING  Closing large rings not implemented
WARNING  Failed to close a ring, minimising on all atoms
INFO     Double bond - adding constraint
INFO     Double bond - adding constraint
INFO     Double bond - adding constraint
INFO     Resetting sites on atom 4
INFO     Rotating 3 sites onto 2 points
INFO     Resetting sites on atom 5
INFO     Rotating 3 sites onto 2 points
INFO     Queue: [5]
INFO     Queuing Dihedral(idxs=[3, 4, 5, 6], φ0=0)
INFO     Have 1 dihedral(s) to rotate
WARNING  Could not apply rotation Dihedral(idxs=[3, 4, 5, 6], φ0=0)
INFO     Queue: [8]
INFO     Queue: []
INFO     Minimising non-bonded repulsion by dihedral rotation
INFO     Have 1 dihedrals to rotate
INFO     Performed final dihedral rotation in: 9.02 ms
INFO     Built 3D in: 63.47 ms
INFO     Generating molecular graph with NetworkX
INFO     Generating molecular graph with NetworkX
INFO     Setting the π bonds in a species
INFO     Setting the stereocentres in a species
WARNING  Could not find a valid valance for Na. Guessing at 6
INFO     Initialisation with SMILES successful. Charge=0, Multiplicity=2, Num. Atoms=21


Comments:

Patch:
diff --git a/autode/smiles/angles.py b/autode/smiles/angles.py
index ffec3bc..0695230 100644
--- a/autode/smiles/angles.py
+++ b/autode/smiles/angles.py
@@ -250,9 +250,10 @@ class Dihedral(Angle):
             graph (nx.Graph):
             atoms (list(autode.atoms.Atom)):
         """
-        return self._find_rot_idxs_from_pair(graph, atoms, pair=self.mid_idxs)
+        return self._find_rot_idxs_from_pair(graph, atoms, pair=self.mid_idxs,
+                                             max_bond_distance=1.5*self.mid_dist)
 
-    def __init__(self, idxs, rot_idxs=None, phi0=None):
+    def __init__(self, idxs, rot_idxs=None, phi0=None, mid_dist=2.0):
         """
         A dihedral constructed from atom indexes and possibly indexes that
         should be rotated, if this dihedral is altered::
@@ -272,9 +273,13 @@ class Dihedral(Angle):
                                          should be rotated else 0
 
             phi0 (float | None): Ideal angle for this dihedral (radians)
+
+            mid_dist (float): Optimum distance between X-Y
         """
         super().__init__(idxs=idxs, rot_idxs=rot_idxs, phi0=phi0)
 
         # Atom indexes of the central two atoms (X, Y)
         _, idx_x, idx_y, _ = idxs
+
         self.mid_idxs = (idx_x, idx_y)
+        self.mid_dist = mid_dist
diff --git a/autode/smiles/builder.py b/autode/smiles/builder.py
index baad827..4aa2593 100644
--- a/autode/smiles/builder.py
+++ b/autode/smiles/builder.py
@@ -300,6 +300,10 @@ class Builder(AtomCollection):
 
             dihedral = Dihedral(dihedral_idxs)
 
+            # Optimum distance between the two middle atoms, used for
+            # determining if a bond exists thus a dihedral can be rotated
+            dihedral.mid_dist = self.bonds.first_involving(*dihedral.mid_idxs).r0
+
             # If both atoms either side of this one are 'pi' atoms e.g. in a
             # benzene ring, then the ideal angle must be 0 to close the ring
             if all(self.atoms[idx].is_pi for idx in dihedral.mid_idxs):
diff --git a/autode/smiles/parser.py b/autode/smiles/parser.py
index 7a55d71..4eded90 100644
--- a/autode/smiles/parser.py
+++ b/autode/smiles/parser.py
@@ -40,9 +40,13 @@ class Parser:
         """Approximate spin multiplicity (2S+1). For multiple unpaired
         electrons will default to a singlet"""
 
-        n_electrons = (sum([atom.atomic_number for atom in self.atoms])
+        n_electrons = (sum([at.atomic_number for at in self.atoms])
                        - self.charge)
 
+        # Atoms have implicit hydrogens, so add them
+        n_electrons += sum(at.n_hydrogens if at.n_hydrogens is not None else 0
+                           for at in self.atoms)
+
         return (n_electrons % 2) + 1
 
     @property
diff --git a/autode/smiles/smiles.py b/autode/smiles/smiles.py
index 63d07c2..895e40b 100644
--- a/autode/smiles/smiles.py
+++ b/autode/smiles/smiles.py
@@ -70,6 +70,8 @@ def init_organic_smiles(molecule, smiles):
     except RuntimeError:
         raise RDKitFailed
 
+    logger.info('Using RDKit to initialise')
+
     molecule.charge = Chem.GetFormalCharge(rdkit_mol)
     molecule.mult = calc_multiplicity(molecule,
                                       NumRadicalElectrons(rdkit_mol))


Test patch:
diff --git a/tests/test_smiles_parser.py b/tests/test_smiles_parser.py
index 4abdf09..4b5abf9 100644
--- a/tests/test_smiles_parser.py
+++ b/tests/test_smiles_parser.py
@@ -400,3 +400,11 @@ def test_ring_connectivity():
 
     # and has two carbon-sulfur bonds
     assert n_c_s_bonds == 2
+
+
+def test_multiplicity_metals():
+
+    parser = Parser()
+
+    parser.parse(smiles='[Na]C1=CC=CC=C1')
+    assert parser.mult == 1