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_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_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_spin_multiplicity(self):
        molecule = Molecule()

        # get_spin_multiplicity() should return 1 before any fragments added to Molecule
        self.assertEqual(molecule.get_spin_multiplicity(), 1)

        fragment0 = Fragment(-1, 3)

        molecule.add_fragment(fragment0)

        # get_spin_multiplicity() should return 3 after first fragment added to Molecule
        self.assertEquals(molecule.get_spin_multiplicity(), 3)

        fragment1 = Fragment(3, 1)

        molecule.add_fragment(fragment1)

        # get_spin_multiplicity() should return 3 after second fragment added to Molecule
        self.assertEquals(molecule.get_spin_multiplicity(), 3)
    def test_get_charge(self):
        molecule = Molecule()

        # get_charge() should return 0 before any fragments added to Molecule
        self.assertEqual(molecule.get_charge(), 0)

        fragment0 = Fragment(-1, 3)

        molecule.add_fragment(fragment0)

        # get_charge() should return -1 after first fragment added to Molecule
        self.assertEquals(molecule.get_charge(), -1)

        fragment1 = Fragment(3, 1)

        molecule.add_fragment(fragment1)

        # get_charge() should return 2 after second fragment added to Molecule
        self.assertEquals(molecule.get_charge(), 2)
    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 xyz_to_molecules(f, config):

    # Keep in mind that you're passing in a file from initializer, not a
    # directory!

    # number of atoms in each fragment
    atoms_per_fragment = []
    # charge of each fragment
    charge_per_fragment = []
    # spin of each fragment
    spin_per_fragment = []

    # Don't try to find the settings.ini, we should now REQUIRE it instead
    '''
    if "settings.ini" in dir_contents:
        # create config object
        config = configparser.SafeConfigParser(allow_no_value=False)

        # read the conflig file into the configuration
        config.read(directory + "/settings.ini")
    '''

    # the for loop just changes the string array to an int array
    atoms_per_fragment = [
        int(atom_count)
        for atom_count in config["molecule"]["fragments"].split(",")
    ]
    charge_per_fragment = [
        int(charge) for charge in config["molecule"]["charges"].split(",")
    ]
    spin_per_fragment = [
        int(spin) for spin in config["molecule"]["spins"].split(",")
    ]

    # define the list of molecules
    molecules = []

    # Duplicate file openings
    '''
    # loop thru all files in directory
    for file_name in dir_contents:
        # check if file_name is an xyz file
        if file_name[-4:] == ".xyz":
            # parse the xyz file
            xyz = open(directory + "/" + file_name, "r")
    '''
    while True:

        # read first line of file, should be num of atoms
        atom_num_line = readline(f)
        # check for end of input
        if atom_num_line == "":
            break

        # check for consistance between number of atoms in xyz file
        # and number of atoms in fragments array
        if int(atom_num_line) != sum(atoms_per_fragment):
            raise ValueError(
                "Atom count specified in xyz file does not match count in ini file"
            )
        # read and discard the comment line
        readline(f, True)

        # init Molecule object
        molecule = Molecule()
        molecule.natoms = atoms_per_fragment
        molecule.charges = charge_per_fragment
        molecule.spins = spin_per_fragment

        # start reading atom lines
        for atom_count, charge, spin in zip(atoms_per_fragment,
                                            charge_per_fragment,
                                            spin_per_fragment):

            # init Fragment object
            fragment = Fragment(charge, spin)

            for i in range(0, atom_count):
                # read a line
                atom_line = readline(f, True)

                # extract atom info from line
                symbol, x, y, z = atom_line.split()

                # add atom to fragment
                fragment.add_atom(Atom(symbol, float(x), float(y), float(z)))

            # add fragment to molecule
            molecule.add_fragment(fragment)

        # add molecule to list of molecules
        molecules.append(molecule)

    return molecules
    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)