def test_get_atoms(self):
        molecule = Molecule()

        # get_atoms() should return list of len 0 before any atoms added to Molecule
        self.assertEqual(len(molecule.get_atoms()), 0)

        fragment0 = Fragment(0, 1)
        atom0 = Atom("H", 0, 0, 0)

        fragment0.add_atom(atom0)

        molecule.add_fragment(fragment0)

        self.assertEqual(molecule.get_atoms()[0], atom0)
        # get_atoms() should return list of len 1 after 1 atom added to Molecule
        self.assertEqual(len(molecule.get_atoms()), 1)

        fragment1 = Fragment(0, 1)
        atom1 = Atom("H", 0, 0, 0)
        atom2 = Atom("He", 234, -0.1, 32)
        atom3 = Atom("He", 34, 43.53, 0)

        fragment1.add_atom(atom1)
        fragment1.add_atom(atom2)
        fragment1.add_atom(atom3)

        molecule.add_fragment(fragment1)

        self.assertEqual(molecule.get_atoms()[0], atom0)
        self.assertEqual(molecule.get_atoms()[1], atom1)
        self.assertEqual(molecule.get_atoms()[2], atom2)
        self.assertEqual(molecule.get_atoms()[3], atom3)
        # get_atoms() should return list of len 4 after 4 atoms added to molecule
        self.assertEqual(len(molecule.get_atoms()), 4)
    def test_get_num_atoms(self):
        molecule = Molecule()

        # get_num_atoms() should return 0 before any atoms added to molecule
        self.assertEqual(molecule.get_num_atoms(), 0)

        fragment0 = Fragment(-1, 2)
        atom0 = Atom("F", 0, 0, 0)

        fragment0.add_atom(atom0)

        molecule.add_fragment(fragment0)

        # get_num_fragments() should return 1 after 1 atom added to molecule
        self.assertEqual(molecule.get_num_atoms(), 1)

        fragment1 = Fragment(-1, 2)
        atom1 = Atom("F", 0, 0, 3)
        atom2 = Atom("Cl", 0, 0, 0)

        fragment1.add_atom(atom1)
        fragment1.add_atom(atom2)

        molecule.add_fragment(fragment1)

        # get_num_fragments() should return 3 after 3 atoms added to molecule
        self.assertEqual(molecule.get_num_atoms(), 3)
    def test_add_fragment_and_get_fragments(self):
        molecule = Molecule()

        # get_fragments() should return list of len 0 before any fragments added to Molecule
        self.assertEqual(len(molecule.get_fragments()), 0)

        fragment0 = Fragment(-3, 2)
        atom0 = Atom("H", 0, 0, 0)

        fragment0.add_atom(atom0)

        molecule.add_fragment(fragment0)

        self.assertEqual(molecule.get_fragments()[0], fragment0)
        # get_fragments() should return list of len 1 after 1 fragment added to Molecule
        self.assertEqual(len(molecule.get_fragments()), 1)

        fragment1 = Fragment(1, 1)
        atom1 = Atom("H", 0, 0, 0)
        atom2 = Atom("He", 234, -0.1, 32)
        atom3 = Atom("He", 34, 43.53, 0)

        fragment1.add_atom(atom1)
        fragment1.add_atom(atom2)
        fragment1.add_atom(atom3)

        molecule.add_fragment(fragment1)

        self.assertEqual(molecule.get_fragments()[0], fragment0)
        self.assertEqual(molecule.get_fragments()[1], fragment1)
        # get_fragments() should return list of len 2 after 2 fragments added to Molecule
        self.assertEqual(len(molecule.get_fragments()), 2)
    def get_optimized_energies(self, method, basis, cp, tag):
        """
        Returns a list of pairs of [molecule, energies] where energies is an array of the form [E0, ...] of molecules in the database with the given
        method, basis, cp and tag and optimized geometries

        % can be used as a wildcard to stand in for any method, basis, cp, or tag. 

        Args:
            method  - retrieve only energies computed with this method
            basis   - retrieve only energies computed with this basis
            cp      - retrieve only energies computed with this coutnerpoise correction
            tag     - retrieve only energies with this tag

        Returns:
            a generator of [molecule, [E0, E1, E2, E01, ...]] pairs from the calculated energies in this database using the given model and tag of optimized geometries
        """

        # get a list of all calculations that have the appropriate method, basis, and cp
        calculation_ids = [
            elements[0] for elements in self.cursor.execute(
                "SELECT ROWID FROM Calculations WHERE model_id=(SELECT ROWID FROM Models WHERE method LIKE ? AND basis LIKE ? AND cp LIKE ? AND tag LIKE ? AND optimized=?)",
                (method, basis, cp, tag, 1)).fetchall()
        ]

        for calculation_id in calculation_ids:

            # get the molecule id corresponding to this calculation
            molecule_id = self.cursor.execute(
                "SELECT molecule_id FROM Calculations WHERE ROWID=?",
                (calculation_id, )).fetchone()[0]

            # Reconstruct the molecule from the information in this database
            molecule = Molecule()

            # loop over all rows in the Fragments table that correspond to this molecule
            for fragment_id, charge, spin in self.cursor.execute(
                    "SELECT ROWID, charge, spin FROM Fragments WHERE molecule_id=?",
                (molecule_id, )).fetchall():
                fragment = Fragment(charge, spin)

                # loop over all rows in the Atoms table that correspond to this fragment
                for symbol, x, y, z in self.cursor.execute(
                        "SELECT symbol, x, y, z FROM Atoms WHERE fragment_id=?",
                    (fragment_id, )).fetchall():
                    fragment.add_atom(Atom(symbol, x, y, z))

                molecule.add_fragment(fragment)

            # get the energies corresponding to this calculation
            energies = [
                energy_index_value_pair[1]
                for energy_index_value_pair in sorted(
                    self.cursor.execute(
                        "SELECT energy_index, energy FROM Energies WHERE calculation_id=?",
                        (calculation_id, )).fetchall())
            ]

            yield molecule, energies
    def test_get_num_atoms(self):
        fragment = Fragment(3, 2)

        # get_num_atoms() should return 0 before any Atoms added to Fragment
        self.assertEqual(fragment.get_num_atoms(), 0)

        fragment.add_atom(Atom("H", 0, 0, 0))

        # get_num_atoms() should return 1 after single atom added to Fragment
        self.assertEqual(fragment.get_num_atoms(), 1)

        fragment.add_atom(Atom("Al", 0, 0, 0))

        # get_num_atoms() should return 2 after second atom added to Fragment
        self.assertEqual(fragment.get_num_atoms(), 2)

        fragment.add_atom(Atom("He", 0, 0, 0))

        # get_num_atoms() should return 3 after third atom added to Fragment
        self.assertEqual(fragment.get_num_atoms(), 3)
    def test_to_standard_xyz(self):
        molecule = Molecule()

        fragment0 = Fragment(-1, 1)
        fragment0.add_atom(Atom("H", 5, 3, 0.00343))
        fragment0.add_atom(Atom("Cl", 2, 0, -13))
        fragment0.add_atom(Atom("He", 6, 2, 0.343))

        molecule.add_fragment(fragment0)

        self.assertEqual(molecule.to_xyz(), fragment0.to_xyz()[:-1])

        fragment1 = Fragment(-2, 1)
        fragment1.add_atom(Atom("Ar", 0.23430523424, -34, -234.5235))

        molecule.add_fragment(fragment1)

        self.assertEqual(molecule.to_xyz(),
                         fragment1.to_xyz() + fragment0.to_xyz()[:-1])

        fragment2 = Fragment(0, 2)
        fragment2.add_atom(Atom("Xe", 0, 0, 0))
        fragment2.add_atom(Atom("Br", 62, 5, 0.001))

        molecule.add_fragment(fragment2)
        self.assertEqual(
            molecule.to_xyz(),
            fragment1.to_xyz() + fragment2.to_xyz() + fragment0.to_xyz()[:-1])
    def test_add_atom_and_get_atoms(self):
        fragment = Fragment(0, 1)

        # get_atom() should return list of length 0 before any atoms added to fragment
        self.assertEqual(len(fragment.get_atoms()), 0)

        atom0 = Atom("H", 0, 0, 0)

        fragment.add_atom(atom0)

        self.assertEqual(fragment.get_atoms()[0], atom0)
        # get_atom() should return list of length 1 after 1 atom added to fragment
        self.assertEqual(len(fragment.get_atoms()), 1)

        atom1 = Atom("Cl", 400, 32, 23)

        fragment.add_atom(atom1)

        self.assertEqual(fragment.get_atoms()[0], atom1)
        self.assertEqual(fragment.get_atoms()[1], atom0)
        # get_atom() should return list of length 2 after 2 atoms added to fragment
        self.assertEqual(len(fragment.get_atoms()), 2)
    def get_missing_energy(self):
        """
        Returns a single Calculation object, which contains the info a user needs to calculate an energy missing in the table.

        The user should calculate the energy, then call either set_energy() or set_failed()

        Args:
            None

        Returns:
            A Calculation object describing the calculation to be performed
        """

        # retrieve a pending job from the Jobs table
        try:
            job_id = self.cursor.execute(
                "SELECT ROWID FROM Jobs WHERE status=?",
                ("pending", )).fetchone()[0]
        except TypeError:
            # this error will be thrown if there are no more energies to calculate, because None[0] throws a TypeError
            return None

        # retrieve the calculation and energy to be calculated for this job from the Energies table
        calculation_id, energy_index = self.cursor.execute(
            "SELECT calculation_id, energy_index FROM Energies WHERE job_id=?",
            (job_id, )).fetchone()

        # retrieve the molecule and model from the Calculations table
        molecule_id, model_id, tag, optimized = self.cursor.execute(
            "SELECT molecule_id, model_id, tag, optimized FROM Calculations WHERE ROWID=?",
            (calculation_id, )).fetchone()

        # retrieve the method, basis, and cp for this model
        method, basis, cp = self.cursor.execute(
            "SELECT method, basis, cp FROM Models WHERE ROWID=?",
            (model_id, )).fetchone()

        # Reconstruct the molecule from the information in this database
        molecule = Molecule()

        # loop over all rows in the Fragments table that correspond to this molecule
        for fragment_id, charge, spin in self.cursor.execute(
                "SELECT ROWID, charge, spin FROM Fragments WHERE molecule_id=?",
            (molecule_id, )).fetchall():
            fragment = Fragment(charge, spin)

            # loop over all rows in the Atoms table that correspond to this fragment
            for symbol, x, y, z in self.cursor.execute(
                    "SELECT symbol, x, y, z FROM Atoms WHERE fragment_id=?",
                (fragment_id, )).fetchall():
                fragment.add_atom(Atom(symbol, x, y, z))

            molecule.add_fragment(fragment)

        # get the indicies of the fragments to include in this calculation
        fragment_indicies = energy_index_to_fragment_indicies(
            energy_index, molecule.get_num_fragments())

        # update the job with its start date and running status
        self.cursor.execute(
            "UPDATE Jobs SET status=?, start_date=? WHERE ROWID=?",
            ("running", datetime.datetime.today().strftime('%Y/%m/%d'),
             job_id))

        return Calculation(molecule, method, basis, True if cp == 1 else False,
                           fragment_indicies, job_id)
    def test_to_xyz(self):
        atom = Atom("H", 0, 0, 0)
        self.assertEqual(
            atom.to_xyz(),
            "H    0.00000000000000e+00   0.00000000000000e+00   0.00000000000000e+00"
        )

        atom = Atom("Ar", 0.34234, -0.342, 1.2334)
        self.assertEqual(
            atom.to_xyz(),
            "Ar   3.42340000000000e-01  -3.42000000000000e-01   1.23340000000000e+00"
        )

        atom = Atom("He", 12.34, 105.34, -0.00432)
        self.assertEqual(
            atom.to_xyz(),
            "He   1.23400000000000e+01   1.05340000000000e+02  -4.32000000000000e-03"
        )

        atom = Atom("C", -2523452, 0.0003453, 34534)
        self.assertEqual(
            atom.to_xyz(),
            "C   -2.52345200000000e+06   3.45300000000000e-04   3.45340000000000e+04"
        )

        atom = Atom("Cl", 0.00000000000068, 0.74576456847823583,
                    34534262462472457756745)
        self.assertEqual(
            atom.to_xyz(),
            "Cl   6.80000000000000e-13   7.45764568478236e-01   3.45342624624725e+22"
        )
    def test_to_xyz(self):
        fragment = Fragment(-2, 2)

        # to_xyz() should return empty string when no atoms are added to fragment
        self.assertEqual(fragment.to_xyz(), "")

        atom0 = Atom("H", 0, 0, 0)

        fragment.add_atom(atom0)

        # to_xyz() should return string of first atom after only 1 atom added
        self.assertEqual(fragment.to_xyz(), atom0.to_xyz() + "\n")

        atom1 = Atom("Cl", 5, 7, -3)

        fragment.add_atom(atom1)

        # to_xyz() should return string of 2 atoms after 2nd atom added
        self.assertEqual(fragment.to_xyz(),
                         atom1.to_xyz() + "\n" + atom0.to_xyz() + "\n")

        atom2 = Atom("Xe", 10.234235, -0.00000234, 2.353523)

        fragment.add_atom(atom2)

        # to_xyz() should return string of 3 atoms after only 3rd atom added
        self.assertEqual(
            fragment.to_xyz(),
            atom1.to_xyz() + "\n" + atom0.to_xyz() + "\n" + atom2.to_xyz() +
            "\n")
    def test_get_name(self):
        atom = Atom("H", 0, 0, 0)
        self.assertEqual(atom.get_name(), "H")

        atom = Atom("Cl", 0, 0, 0)
        self.assertEqual(atom.get_name(), "Cl")

        atom = Atom("He", 0, 0, 0)
        self.assertEqual(atom.get_name(), "He")

        atom = Atom("Ar", 0, 0, 0)
        self.assertEqual(atom.get_name(), "Ar")