Repository: duartegroup/autodE

Issue:
Sum formulas as filenames lead to overwritten structures in isomerizations
**Describe the bug**
Hi there. In the output folder, file names are given as sum formulas. This creates issues as soon as we look into isomerizations. In the csv file there is no way of telling which of the identically labelled line is what. But much more problematic: both in "reactants_and_products" and in "output" only one set of calculations/one structure will get saved. A quick hotfix would be to check initally if several molecules of the same sum formula exist and add a "b" or a "_1" or something to all filenames.

**To Reproduce**
This will happen also in the example that is given in the Github manual.
```
import autode as ade
ade.Config.n_cores = 8

r = ade.Reactant(name='reactant', smiles='CC[C]([H])[H]')
p = ade.Product(name='product', smiles='C[C]([H])C')

reaction = ade.Reaction(r, p, name='1-2_shift')
reaction.calculate_reaction_profile()  # creates 1-2_shift/ and saves profile
```

Expected behavior
Saving all calculations and structures, ideally in a distinguishable fashion.

Environment

Operating System: CentOS-7
Python version: 3.9.10
autodE version: 1.2.1

Comments:

Patch:
diff --git a/autode/__init__.py b/autode/__init__.py
index cb84893..e18739b 100644
--- a/autode/__init__.py
+++ b/autode/__init__.py
@@ -33,7 +33,7 @@ So, you want to bump the version.. make sure the following steps are followed
   - Merge when tests pass
 """
 
-__version__ = '1.2.2'
+__version__ = '1.2.3'
 
 
 __all__ = [
diff --git a/autode/reactions/reaction.py b/autode/reactions/reaction.py
index 9cc605f..63a0d99 100644
--- a/autode/reactions/reaction.py
+++ b/autode/reactions/reaction.py
@@ -75,6 +75,7 @@ class Reaction:
 
         self._check_solvent()
         self._check_balance()
+        self._check_names()
 
     def __str__(self):
         """Return a very short 6 character hash of the reaction, not guaranteed
@@ -147,7 +148,7 @@ class Reaction:
                                                        enthalpy=enthalpy)
         return None
 
-    def _check_balance(self):
+    def _check_balance(self) -> None:
         """Check that the number of atoms and charge balances between reactants
          and products. If they don't then raise excpetions
         """
@@ -170,7 +171,7 @@ class Reaction:
         self.charge = total(self.reacs, 'charge')
         return None
 
-    def _check_solvent(self):
+    def _check_solvent(self) -> None:
         """
         Check that all the solvents are the same for reactants and products.
         If self.solvent is set then override the reactants and products
@@ -207,7 +208,28 @@ class Reaction:
                     f'{self.solvent.name}')
         return None
 
-    def _init_from_smiles(self, reaction_smiles):
+    def _check_names(self) -> None:
+        """
+        Ensure there is no clashing names of reactants and products, which will
+        cause problems when conformers are generated and output is printed
+        """
+        all_names = [mol.name for mol in self.reacs + self.prods]
+
+        if len(set(all_names)) == len(all_names):  # Everything is unique
+            return
+
+        logger.warning('Names in reactants and products are not unique. '
+                       'Adding prefixes')
+
+        for i, reac in enumerate(self.reacs):
+            reac.name = f'r{i}_{reac.name}'
+
+        for i, prod in enumerate(self.prods):
+            prod.name = f'p{i}_{prod.name}'
+
+        return None
+
+    def _init_from_smiles(self, reaction_smiles) -> None:
         """
         Initialise from a SMILES string of the whole reaction e.g.::
 
@@ -237,7 +259,7 @@ class Reaction:
 
         return None
 
-    def _init_from_molecules(self, molecules):
+    def _init_from_molecules(self, molecules) -> None:
         """Set the reactants and products from a set of molecules"""
 
         self.reacs = [mol for mol in molecules
@@ -248,7 +270,7 @@ class Reaction:
 
         return None
 
-    def _reasonable_components_with_energy(self):
+    def _reasonable_components_with_energy(self) -> None:
         """Generator for components of a reaction that have sensible geometries
         and also energies"""
 
diff --git a/doc/changelog.rst b/doc/changelog.rst
index ec75310..3449692 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -2,6 +2,18 @@ Changelog
 =========
 
 
+1.2.3
+--------
+----------
+
+Bugfix release.
+
+
+Bug Fixes
+*********
+- Fixes clashing names for a reaction initialised explicitly from molecules without defined names
+
+
 1.2.2
 --------
 ----------
diff --git a/setup.py b/setup.py
index 9c0e649..70729e8 100644
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,7 @@ extensions = [Extension('cconf_gen', ['autode/conformers/cconf_gen.pyx']),
                         extra_link_args=["-std=c++11"])]
 
 setup(name='autode',
-      version='1.2.2',
+      version='1.2.3',
       packages=['autode',
                 'autode.conformers',
                 'autode.pes',
@@ -44,5 +44,4 @@ setup(name='autode',
       url='https://github.com/duartegroup/autodE',
       license='MIT',
       author='Tom Young',
-      author_email='tom.young@chem.ox.ac.uk',
-      description='Automated Reaction Profile Generation')
+      description='Automated reaction profile generation')


Test patch:
diff --git a/tests/test_reaction_class.py b/tests/test_reaction_class.py
index 2d2c023..5c10f57 100644
--- a/tests/test_reaction_class.py
+++ b/tests/test_reaction_class.py
@@ -464,3 +464,11 @@ def test_same_composition():
     r3 = reaction.Reaction(Reactant(name='h2', atoms=[Atom('H', 1.0, 0.0, 0.0)]),
                            Product(name='h2', atoms=[Atom('H', 1.0, 0.0, 0.0)]))
     assert not r1.has_identical_composition_as(r3)
+
+
+def test_name_uniqueness():
+
+    rxn = reaction.Reaction(Reactant(smiles='CC[C]([H])[H]'),
+                            Product(smiles='C[C]([H])C'))
+
+    assert rxn.reacs[0].name != rxn.prods[0].name