Exemple #1
0
    def hessian(self):
        """Extract the Hessian matrix from the Gaussian fchk file."""

        # Make the fchk file first
        with open("formchck.log", "w+") as formlog:
            sp.run("formchk lig.chk lig.fchk",
                   shell=True,
                   stdout=formlog,
                   stderr=formlog)

        with open("lig.fchk", "r") as fchk:

            lines = fchk.readlines()

            # Improperly formatted Hessian (converted to square numpy array later)
            hessian_list = []
            start, end = None, None

            for count, line in enumerate(lines):
                if line.startswith("Cartesian Force Constants"):
                    start = count + 1
                if line.startswith("Nonadiabatic coupling"):
                    if end is None:
                        end = count
                if line.startswith("Dipole Moment"):
                    if end is None:
                        end = count

            if not start and end:
                raise EOFError(
                    "Cannot locate Hessian matrix in lig.fchk file.")

            conversion = constants.HA_TO_KCAL_P_MOL / (constants.BOHR_TO_ANGS**
                                                       2)
            for line in lines[start:end]:
                # Extend the list with the converted floats from the file, splitting on spaces and removing '\n' tags.
                hessian_list.extend([
                    float(num) * conversion
                    for num in line.strip("\n").split()
                ])

        hess_size = 3 * len(self.molecule.atoms)
        hessian = np.zeros((hess_size, hess_size))

        # Rewrite Hessian to full, symmetric 3N * 3N matrix rather than list with just the non-repeated values.
        m = 0
        for i in range(hess_size):
            for j in range(i + 1):
                hessian[i, j] = hessian_list[m]
                hessian[j, i] = hessian_list[m]
                m += 1

        check_symmetry(hessian)

        return hessian
Exemple #2
0
    def hessian(self):
        """
        Parses the Hessian from the output.dat file (from psi4) into a numpy array;
        performs check to ensure it is symmetric;
        has some basic error handling for if the file is missing data etc.
        """

        hess_size = 3 * len(self.molecule.atoms)

        # output.dat is the psi4 output file.
        with open('output.dat', 'r') as file:

            lines = file.readlines()

            for count, line in enumerate(lines):
                if '## Hessian' in line or '## New Matrix (Symmetry' in line:
                    # Set the start of the hessian to the row of the first value.
                    hess_start = count + 5
                    break
            else:
                raise EOFError('Cannot locate Hessian matrix in output.dat file.')

            # Check if the hessian continues over onto more lines (i.e. if hess_size is not divisible by 5)
            extra = 0 if hess_size % 5 == 0 else 1

            # hess_length: # of cols * length of each col
            #            + # of cols - 1 * #blank lines per row of hess_vals
            #            + # blank lines per row of hess_vals if the hess_size continues over onto more lines.
            hess_length = (hess_size // 5) * hess_size + (hess_size // 5 - 1) * 3 + extra * (3 + hess_size)

            hess_end = hess_start + hess_length

            hess_vals = []

            for file_line in lines[hess_start:hess_end]:
                # Compile lists of the 5 Hessian floats for each row.
                # Number of floats in last row may be less than 5.
                # Only the actual floats are added, not the separating numbers.
                row_vals = [float(val) for val in file_line.split() if len(val) > 5]
                hess_vals.append(row_vals)

            # Remove blank list entries
            hess_vals = [elem for elem in hess_vals if elem]

            reshaped = []

            # Convert from list of (lists, length 5) to 2d array of size hess_size x hess_size
            for old_row in range(hess_size):
                new_row = []
                for col_block in range(hess_size // 5 + extra):
                    new_row += hess_vals[old_row + col_block * hess_size]

                reshaped.append(new_row)

            hess_matrix = np.array(reshaped)

            # Cache the unit conversion.
            conversion = constants.HA_TO_KCAL_P_MOL / (constants.BOHR_TO_ANGS ** 2)
            # Element-wise multiplication
            hess_matrix *= conversion

            check_symmetry(hess_matrix)

            return hess_matrix
Exemple #3
0
    def call_qcengine(self, engine, driver, input_type):
        """
        Using the created schema, run a particular engine, specifying the driver (job type).
        e.g. engine: geo, driver: energies.
        :param engine: The engine to be used psi4 geometric
        :param driver: The calculation type to be done e.g. energy, gradient, hessian, properties
        :param input_type: The part of the molecule object that should be used when making the schema
        :return: The required driver information
        """

        mol = self.generate_qschema(input_type=input_type)

        # Call psi4 for energy, gradient, hessian or property calculations
        if engine == 'psi4':
            psi4_task = qcel.models.ResultInput(
                molecule=mol,
                driver=driver,
                model={
                    'method': self.molecule.theory,
                    'basis': self.molecule.basis
                },
                keywords={'scf_type': 'df'},
            )

            ret = qcng.compute(psi4_task,
                               'psi4',
                               local_options={
                                   'memory': self.molecule.memory,
                                   'ncores': self.molecule.threads
                               })

            if driver == 'hessian':
                hess_size = 3 * len(self.molecule.atoms)
                conversion = constants.HA_TO_KCAL_P_MOL / (
                    constants.BOHR_TO_ANGS**2)
                hessian = np.reshape(ret.return_result,
                                     (hess_size, hess_size)) * conversion
                check_symmetry(hessian)

                return hessian

            else:
                return ret.return_result

        # Call geometric with psi4 to optimise a molecule
        elif engine == 'geometric':
            geo_task = {
                'schema_name': 'qcschema_optimization_input',
                'schema_version': 1,
                'keywords': {
                    'coordsys': 'tric',
                    'maxiter': self.molecule.iterations,
                    'program': 'psi4',
                    'convergence_set': self.molecule.convergence,
                },
                'input_specification': {
                    'schema_name': 'qcschema_input',
                    'schema_version': 1,
                    'driver': 'gradient',
                    'model': {
                        'method': self.molecule.theory,
                        'basis': self.molecule.basis
                    },
                    'keywords': {},
                },
                'initial_molecule': mol,
            }
            return qcng.compute_procedure(geo_task,
                                          'geometric',
                                          return_dict=True,
                                          local_options={
                                              'memory': self.molecule.memory,
                                              'ncores': self.molecule.threads
                                          })

        else:
            raise KeyError(
                'Invalid engine type provided. Please use "geo" or "psi4".')
Exemple #4
0
    def call_qcengine(self, engine, driver):
        """
        Using the created schema, run a particular engine, specifying the driver (job type).
        e.g. engine: geo, driver: energies.
        :param engine: The engine to be used psi4 geometric
        :param driver: The calculation type to be done e.g. energy, gradient, hessian, properties
        :return: The required driver information
        """

        mol = self.generate_qschema()
        options = {"memory": self.molecule.memory, "ncores": self.molecule.threads}

        # Call psi4 for energy, gradient, hessian or property calculations
        if engine == "psi4":
            psi4_task = qcel.models.AtomicInput(
                molecule=mol,
                driver=driver,
                model={"method": self.molecule.theory, "basis": self.molecule.basis},
                keywords={"scf_type": "df"},
            )

            ret = qcng.compute(psi4_task, "psi4", local_options=options)

            if driver == "hessian":
                hess_size = 3 * len(self.molecule.atoms)
                conversion = constants.HA_TO_KCAL_P_MOL / (constants.BOHR_TO_ANGS ** 2)
                hessian = (
                    np.reshape(ret.return_result, (hess_size, hess_size)) * conversion
                )
                check_symmetry(hessian)

                return hessian

            else:
                return ret.return_result

        # Call geometric with psi4 to optimise a molecule
        elif engine == "geometric":
            geo_task = {
                "schema_name": "qcschema_optimization_input",
                "schema_version": 1,
                "keywords": {
                    "coordsys": "dlc",
                    "maxiter": self.molecule.iterations,
                    "program": "psi4",
                    "convergence_set": self.molecule.convergence,
                },
                "input_specification": {
                    "schema_name": "qcschema_input",
                    "schema_version": 1,
                    "driver": "gradient",
                    "model": {
                        "method": self.molecule.theory,
                        "basis": self.molecule.basis,
                    },
                    "keywords": {},
                },
                "initial_molecule": mol,
            }
            return qcng.compute_procedure(
                geo_task, "geometric", return_dict=True, local_options=options
            )

        elif engine == "torchani":
            ani_task = qcel.models.AtomicInput(
                molecule=mol,
                driver=driver,
                model={"method": "ANI1x", "basis": None},
                keywords={"scf_type": "df"},
            )

            return qcng.compute(
                ani_task, "torchani", local_options=options
            ).return_result

        else:
            raise KeyError(
                'Invalid engine type provided. Please use "geometric", "psi4" or "torchani".'
            )