Exemplo n.º 1
0
def load_molecule_g98fchk(fn_freq, fn_ener=None, energy=None):
    """Load a molecule from Gaussian98 formatted checkpoint files.

       Arguments:
         | ``fn_freq`` -- The formatted checkpoint file of the frequency job.

       Optional arguments:
         | ``fn_ener`` -- The formatted checkpoint file of a single point
                          computation for the energy. When not given, the energy
                          is taken from the frequency job.
         | ``energy`` -- Override the energy from the formatted checkpoint file
                         with the given value.
    """

    fchk_freq = FCHKFile(fn_freq,
                         ignore_errors=True,
                         field_labels=[
                             "Cartesian Force Constants", "Total Energy",
                             "Multiplicity", "Cartesian Gradient"
                         ])
    if fn_ener is None:
        fchk_ener = fchk_freq
    else:
        fchk_ener = FCHKFile(fn_ener,
                             ignore_errors=True,
                             field_labels=["Total Energy"])
    masses = np.array([g98_masses[n - 1] for n in fchk_freq.molecule.numbers])
    if energy is None:
        energy = fchk_ener.fields["Total Energy"]

    return Molecule(
        fchk_freq.molecule.numbers,
        fchk_freq.molecule.coordinates,
        masses,
        energy,
        np.reshape(np.array(fchk_freq.fields["Cartesian Gradient"]),
                   (len(fchk_freq.molecule.numbers), 3)),
        fchk_freq.get_hessian(),
        fchk_freq.fields["Multiplicity"],
        None,  # gaussian is very poor at computing the rotational symmetry number
        False,
    )
Exemplo n.º 2
0
def load_molecule_pcgamess_punch(fn_freq, energy=None, multiplicity=1, symmetry_number=None):
    """Load a molecule from a PCGAMESS punch file

       Arguments:
         | fn_freq  --  punch file of the frequency job
    """

    punch = PunchFile(fn_freq)
    if energy is None:
        energy = punch.energy
    return Molecule(
        punch.numbers,
        punch.coordinates,
        punch.masses,
        energy,
        punch.gradient,
        punch.hessian,
        multiplicity,
        symmetry_number,
    )
Exemplo n.º 3
0
def load_molecule_cp2k(fn_sp, fn_freq, multiplicity=1, is_periodic=True):
    """Load a molecule with the Hessian from a CP2K computation

       Arguments:
        | fn_sp   --  The filename of the single point .out file containing the
                      energy and the forces.
        | fn_freq  --  The filename of the frequency .out file containing the
                       hessian

       Optional arguments:
        | multiplicity  --  The spin multiplicity of the electronic system
                            [default=1]
        | is_periodic  --  True when the system is periodic in three dimensions.
                           False when the systen is aperiodic. [default=True]
        | unit_cell  --  The unit cell vectors for periodic structures
    """

    # auxiliary routine to read atoms
    def atom_helper(f):
        # skip some lines
        for i in xrange(3):
            f.readline()
        # read the atom lines until an empty line is encountered
        numbers = []
        coordinates = []
        masses = []
        while True:
            line = f.readline()
            if len(line.strip()) == 0:
                break
            symbol = line[14:19].strip()[:2]
            atom = periodic[symbol]
            if atom is None:
                symbol = symbol[:1]
                atom = periodic[symbol]
            if atom is None:
                numbers.append(0)
            else:
                numbers.append(atom.number)
            coordinates.append(
                [float(line[22:33]),
                 float(line[34:45]),
                 float(line[46:57])])
            masses.append(float(line[72:]))

        numbers = np.array(numbers)
        coordinates = np.array(coordinates) * angstrom
        masses = np.array(masses) * amu
        return numbers, coordinates, masses

    # auxiliary routine to read forces
    def force_helper(f, skip, offset):
        # skip some lines
        for i in xrange(skip):
            f.readline()
        # Read the actual forces
        tmp = []
        while True:
            line = f.readline()
            if line == "\n":
                break
            if line == "":
                raise IOError("End of file while reading gradient (forces).")
            words = line.split()
            try:
                tmp.append([
                    float(words[offset]),
                    float(words[offset + 1]),
                    float(words[offset + 2])
                ])
            except StandardError:
                break
        return -np.array(tmp)  # force to gradient

    # go through the single point file: energy and gradient
    energy = None
    gradient = None
    with open(fn_sp) as f:
        while True:
            line = f.readline()
            if line == "":
                break
            if line.startswith(" ENERGY|"):
                energy = float(line[58:])
            elif line.startswith(" MODULE") and "ATOMIC COORDINATES" in line:
                numbers, coordinates, masses = atom_helper(f)
            elif line.startswith(" FORCES|"):
                gradient = force_helper(f, 0, 1)
                break
            elif line.startswith(' ATOMIC FORCES in [a.u.]'):
                gradient = force_helper(f, 2, 3)
                break
        if energy is None or gradient is None:
            raise IOError(
                "Could not read energy and/or gradient (forces) from single point file."
            )

    # go through the freq file: lattic vectors and hessian
    with open(fn_freq) as f:
        vectors = np.zeros((3, 3), float)
        for line in f:
            if line.startswith(" CELL"):
                break
        for axis in range(3):
            line = f.next()
            vectors[:, axis] = np.array(
                [float(line[29:39]),
                 float(line[39:49]),
                 float(line[49:59])])
        unit_cell = UnitCell(vectors * angstrom)

        free_indices = _load_free_low(f)
        if len(free_indices) > 0:
            total_size = coordinates.size
            free_size = len(free_indices)
            hessian = np.zeros((total_size, total_size), float)
            i2 = 0
            while i2 < free_size:
                num_cols = min(5, free_size - i2)
                f.next()  # skip two lines
                f.next()
                for j in xrange(free_size):
                    line = f.next()
                    words = line.split()
                    for i1 in xrange(num_cols):
                        hessian[free_indices[i2 + i1], free_indices[j]] = \
                            float(words[i1 + 2])
                i2 += num_cols
        else:
            raise IOError("Could not read hessian from freq file.")

    # symmetrize
    hessian = 0.5 * (hessian + hessian.transpose())
    # cp2k prints a transformed hessian, here we convert it back to the normal
    # hessian in atomic units.
    conv = 1e-3 * np.array([masses, masses, masses]).transpose().ravel()**0.5
    hessian *= conv
    hessian *= conv.reshape((-1, 1))

    return Molecule(numbers,
                    coordinates,
                    masses,
                    energy,
                    gradient,
                    hessian,
                    multiplicity,
                    0,
                    is_periodic,
                    unit_cell=unit_cell)
Exemplo n.º 4
0
def load_molecule_qchem(qchemfile,
                        hessfile=None,
                        multiplicity=1,
                        is_periodic=False):
    """Load a molecule from a Q-Chem frequency run

       Arguments:
        | qchemfile  --  Filename of the Q-Chem computation output.

       Optional arguments:
        | hessfile  --  Filename of a separate Hessian file.
        | multiplicity  --  The spin multiplicity of the electronic system
                            [default=1]
        | is_periodic  --  True when the system is periodic in three dimensions.
                           False when the systen is nonperiodic. [default=False]

       Whether the Hessian is printed to a separate Hessian file, depends on the
       used version of Q-Chem. The use of the separate Hessian file is slightly
       more accurate, because the number of printed digits is higher than in the
       Q-Chem output file.

       **Warning**

       At present, the gradient is set to a Nx3 array of zero values, since the
       gradient is not printed out in the Q-Chem output file in general. This
       means that the value of the gradient should be checked before applying
       methods designed for partially optimized structures (currently PHVA,
       MBH and PHVA_MBH).
    """
    # TODO fill in keyword for printing hessian
    f = file(qchemfile)
    # get coords
    for line in f:
        if line.strip().startswith("Standard Nuclear Orientation (Angstroms)"):
            break
    f.next()
    f.next()
    positions = []
    symbols = []
    for line in f:
        if line.strip().startswith("----"): break
        words = line.split()
        symbols.append(words[1])
        coor = [float(words[2]), float(words[3]), float(words[4])]
        positions.append(coor)
    positions = np.array(positions) * angstrom
    N = len(positions)  #nb of atoms

    numbers = np.zeros(N, int)
    for i, symbol in enumerate(symbols):
        numbers[i] = periodic[symbol].number
    #masses = np.zeros(N,float)
    #for i, symbol in enumerate(symbols):
    #    masses[i] = periodic[symbol].mass

    # grep the SCF energy
    energy = None
    for line in f:
        if line.strip().startswith("Cycle       Energy         DIIS Error"):
            break
    for line in f:
        if line.strip().endswith("met"):
            energy = float(line.split()[1])  # in hartree
            break

    # get Hessian
    hessian = np.zeros((3 * N, 3 * N), float)
    if hessfile is None:
        for line in f:
            if line.strip().startswith(
                    "Hessian of the SCF Energy") or line.strip().startswith(
                        "Final Hessian"):
                break
        nb = int(np.ceil(N * 3 / 6))
        for i in range(nb):
            f.next()
            row = 0
            for line in f:
                words = line.split()
                hessian[row, 6 * i:6 * (i + 1)] = np.array(
                    sum([[float(word)] for word in words[1:]],
                        []))  #/ angstrom**2
                row += 1
                if row >= 3 * N: break

    # get masses
    masses = np.zeros(N, float)
    for line in f:
        if line.strip().startswith("Zero point vibrational"):
            break
    f.next()
    count = 0
    for line in f:
        masses[count] = float(line.split()[-1]) * amu
        count += 1
        if count >= N: break

    # get Symm Nb
    for line in f:
        if line.strip().startswith("Rotational Symmetry Number is"):
            break
    symmetry_number = int(line.split()[-1])
    f.close()

    # or get Hessian from other file
    if hessfile is not None:
        f = file(hessfile)
        row = 0
        col = 0
        for line in f:
            hessian[row, col] = float(
                line.split()[0]) * 1000 * calorie / avogadro / angstrom**2
            col += 1
            if col >= 3 * N:
                row += 1
                col = row
        f.close()
        for i in range(len(hessian)):
            for j in range(0, i):
                hessian[i, j] = hessian[j, i]

    # get gradient   TODO
    gradient = np.zeros((N, 3), float)

    return Molecule(numbers, positions, masses, energy, gradient, hessian,
                    multiplicity, symmetry_number, is_periodic)
Exemplo n.º 5
0
def load_molecule_vasp(contcar,
                       outcar_freq,
                       outcar_energy=None,
                       energy=None,
                       multiplicity=1,
                       is_periodic=True):
    """Load a molecule from VASP 4.6.X and 5.3.X output files

       Arguments:
        | contcar  --  A CONTCAR file with the structure used as POSCAR file for the
                       Hessian/frequency calculation in VASP. Do not use the CONTCAR file
                       generated by the frequency calculation. Use the CONTCAR from the
                       preceding geometry optimization instead.
        | outcar_freq  --  The OUTCAR file of the Hessian/frequency calculation. Also the
                           gradient and the energy are read from this file. The energy
                           without entropy (but not the extrapolation to sigma=0) is used.

       Optional arguments:
        | outcar_energy  --  When given, the (first) energy without entropy is read from
                             this file (not the extrapolation to sigma=0) instead of
                             reading the energy from the freq output
        | energy  --  The potential energy, which overrides the contents of outcar_freq.
        | multiplicity  --  The spin multiplicity of the electronic system
                            [default=1]
        | is_periodic  --  True when the system is periodic in three dimensions.
                           False when the systen is nonperiodic. [default=True].
    """

    # auxiliary function to read energy:
    def read_energy_without_entropy(f):
        # Go to the first energy
        for line in f:
            if line.startswith(
                    '  FREE ENERGIE OF THE ION-ELECTRON SYSTEM (eV)'):
                break
        # Skip three lines and read energy
        next(f)
        next(f)
        next(f)
        return float(next(f).split()[3]) * electronvolt

    # Read atomic symbols, coordinates and cell vectors from CONTCAR
    symbols = []
    coordinates = []
    with open(contcar) as f:
        # Skip title.
        next(f).strip()

        # Read scale for rvecs.
        rvec_scale = float(next(f))
        # Read rvecs. VASP uses one row per cell vector.
        rvecs = np.fromstring(next(f) + next(f) + next(f),
                              sep=' ').reshape(3, 3)
        rvecs *= rvec_scale * angstrom
        unit_cell = UnitCell(rvecs)

        # Read symbols
        unique_symbols = next(f).split()
        # Read atom counts per symbol
        symbol_counts = [int(w) for w in next(f).split()]
        assert len(symbol_counts) == len(unique_symbols)
        natom = sum(symbol_counts)
        # Construct array with atomic numbers.
        numbers = []
        for iunique in range(len(unique_symbols)):
            number = periodic[unique_symbols[iunique]].number
            numbers.extend([number] * symbol_counts[iunique])
        numbers = np.array(numbers)

        # Check next line
        while next(f) != 'Direct\n':
            continue

        # Load fractional coordinates
        fractional = np.zeros((natom, 3), float)
        for iatom in range(natom):
            words = next(f).split()
            fractional[iatom, 0] = float(words[0])
            fractional[iatom, 1] = float(words[1])
            fractional[iatom, 2] = float(words[2])
        coordinates = unit_cell.to_cartesian(fractional)

    if outcar_energy is not None and energy is None:
        with open(outcar_energy) as f:
            energy = read_energy_without_entropy(f)

    # Read energy, gradient, Hessian and masses from outcar_freq. Note that the first
    # energy/force calculation is done on the unperturbed input structure.
    with open(outcar_freq) as f:
        # Loop over the POTCAR sections in the OUTCAR file
        number = None
        masses = np.zeros(natom, float)
        while True:
            line = next(f)
            if line.startswith('   VRHFIN ='):
                symbol = line[11:line.find(':')].strip()
                number = periodic[symbol].number
            elif line.startswith('   POMASS ='):
                mass = float(line[11:line.find(';')]) * amu
                masses[numbers == number] = mass
            elif number is not None and line.startswith(
                    '------------------------------'):
                assert masses.min() > 0
                break

        # Go to the first gradient
        for line in f:
            if line.startswith(' POSITION'):
                break
        # Skip one line and read the gradient
        next(f)
        gradient = np.zeros((natom, 3), float)
        gunit = electronvolt / angstrom
        for iatom in range(natom):
            words = next(f).split()
            gradient[iatom, 0] = -float(words[3]) * gunit
            gradient[iatom, 1] = -float(words[4]) * gunit
            gradient[iatom, 2] = -float(words[5]) * gunit

        if energy is None:
            energy = read_energy_without_entropy(f)

        # Go to the second derivatives
        for line in f:
            if line.startswith(' SECOND DERIVATIVES (NOT SYMMETRIZED)'): break

        # Skip one line.
        next(f)

        # Load free atoms (not fixed in space).
        keys = next(f).split()
        nfree_dof = len(keys)
        indices_free = [
            3 * int(key[:-1]) + {
                'X': 0,
                'Y': 1,
                'Z': 2
            }[key[-1]] - 3 for key in keys
        ]
        assert nfree_dof % 3 == 0

        # Load the actual Hessian
        hunit = electronvolt / angstrom**2
        hessian = np.zeros((3 * natom, 3 * natom), float)
        for ifree0 in range(nfree_dof):
            line = next(f)
            irow = indices_free[ifree0]
            # skip first col
            words = line.split()[1:]
            assert len(words) == nfree_dof
            for ifree1 in range(nfree_dof):
                icol = indices_free[ifree1]
                hessian[irow, icol] = -float(words[ifree1]) * hunit

        # Symmetrize the Hessian
        hessian = 0.5 * (hessian + hessian.T)

    return Molecule(numbers,
                    coordinates,
                    masses,
                    energy,
                    gradient,
                    hessian,
                    multiplicity=multiplicity,
                    periodic=is_periodic,
                    unit_cell=unit_cell)
Exemplo n.º 6
0
def load_molecule_g03fchk(fn_freq, fn_ener=None, fn_vdw=None, energy=None, fn_punch=None):
    """Load a molecule from Gaussian03 formatted checkpoint files.

       Arguments:
         | ``fn_freq`` -- the formatted checkpoint file of the frequency job

       Optional arguments:
         | ``fn_ener`` -- the formatted checkpoint file of a single point
                          computation for the energy. When not given, the energy
                          is taken from the frequency job.
         | ``fn_vdw`` -- An orca output file containing a Van der Waals
                         correction for the energy.
         | ``energy`` -- Override the energy from the formatted checkpoint file
                         with the given value.
         | ``punch`` -- A Gaussian derivatives punch file. When given, the
                        gradient and the Hessian are read from this file
                        instead.
    """

    fchk_freq = FCHKFile(fn_freq, ignore_errors=True, field_labels=[
        "Cartesian Force Constants", "Real atomic weights", "Total Energy",
        "Multiplicity", "Cartesian Gradient", "MicOpt",
    ])
    if fn_ener is None:
        fchk_ener = fchk_freq
    else:
        fchk_ener = FCHKFile(fn_ener, ignore_errors=True, field_labels=[
            "Total Energy"
        ])
    if energy is None:
        energy = fchk_ener.fields["Total Energy"]
    vdw = 0
    if fn_vdw is not None:
        from tamkin.io.dispersion import load_dftd_orca
        vdw = load_dftd_orca(fn_vdw)

    natom = fchk_freq.molecule.size
    if fchk_freq.molecule.size == 1 and \
       "Cartesian Force Constants" not in fchk_freq.fields:
        gradient = np.zeros((1,3), float)
        hessian = np.zeros((3,3), float)
    elif fn_punch is None:
        gradient = fchk_freq.fields["Cartesian Gradient"].copy()
        gradient.shape = (natom, 3)
        hessian = fchk_freq.get_hessian()
    else:
        gradient = np.zeros((natom, 3), float)
        hessian = np.zeros((3*natom, 3*natom), float)
        iterator = iter_floats_file(fn_punch)
        for i in xrange(natom):
            for j in xrange(3):
                gradient[i,j] = iterator.next()
        for i in xrange(3*natom):
            for j in xrange(i+1):
                v = iterator.next()
                hessian[i,j] = v
                hessian[j,i] = v

    if "MicOpt" in fchk_freq.fields:
        fixed = (fchk_freq.fields["MicOpt"] == -2).nonzero()[0]
        if len(fixed) == 0:
            fixed = None
    else:
        fixed = None

    return Molecule(
        fchk_freq.molecule.numbers,
        fchk_freq.molecule.coordinates,
        fchk_freq.fields["Real atomic weights"]*amu,
        energy+vdw,
        gradient,
        hessian,
        fchk_freq.fields["Multiplicity"],
        None, # gaussian is very poor at computing the rotational symmetry number
        False,
        title=fchk_freq.title,
        fixed=fixed,
    )
Exemplo n.º 7
0
def load_molecule_charmm(charmmfile_cor, charmmfile_hess, is_periodic=False):
    """Read from Hessian-CHARMM-file format

       Arguments:
        | charmmfile_cor  --  the filename of the .cor file
        | charmmfile_hess  --  the filename of the Hessian file

       Optional argument:
        | is_periodic  --  True when the system is periodic in three dimensions.
                           False when the systen is aperiodic. [default=True]
    """
    f = file(charmmfile_hess)
    # skip lines if they start with a *
    while True:
        line = f.readline()
        if not line.startswith("*"): break
    N = int(line.split()[-1])
    assert N > 0  # nb of atoms should be > 0

    energy = float(f.readline().split()[-1]) * 1000 * calorie / avogadro

    gradient = np.zeros((N, 3), float)
    for i, line in enumerate(f):
        words = line.split()
        gradient[i, :] = [float(word) for word in words]
        if i == (N - 1):
            break
    gradient *= 1000 * calorie / avogadro / angstrom
    hessian = np.zeros((3 * N, 3 * N), float)
    row = 0
    col = 0
    for line in f:
        element = float(line.split()[-1])
        hessian[row, col] = element
        hessian[col, row] = element
        col += 1
        if col >= 3 * N:  #if this new col doesn't exist
            row += 1  #go to next row
            col = row  #to diagonal element
            if row >= 3 * N:  #if this new row doesn't exist
                break
    hessian = hessian * 1000 * calorie / avogadro / angstrom**2

    positions = np.zeros((N, 3), float)
    for i, line in enumerate(f):
        words = line.split()
        positions[i, :] = [float(word) * angstrom for word in words]
        if i == (N - 1):
            break
    f.close()

    # Read from coordinates-CHARMM-file
    # format:  header lines, which start with *
    #          N lines with   - mass in last column
    #                         - atomic type in 4th column
    f = file(charmmfile_cor)
    masses = np.zeros(N, float)
    symbols = []
    for line in f:
        if not line.startswith("*"):  # skip header lines
            break
    for i, line in enumerate(f):
        words = line.split()
        masses[i] = float(words[-1]) * amu  # mass
        symbols.append(words[3])  # symbol
        if i == (N - 1):
            break
    f.close()

    # get corresponding atomic numbers
    mass_table = np.zeros(len(periodic))
    for i in xrange(1, len(mass_table)):
        m1 = periodic[i].mass
        if m1 is None:
            m1 = 200000.0
        m2 = periodic[i + 1].mass
        if m2 is None:
            m2 = 200000.0
        mass_table[i] = 0.5 * (m1 + m2)
    atomicnumbers = np.zeros(N, int)
    for i, mass in enumerate(masses):
        atomicnumbers[i] = mass_table.searchsorted(mass)

    return Molecule(
        atomicnumbers,
        positions,
        masses,
        energy,
        gradient,
        hessian,
        1,  # multiplicity
        symmetry_number=1,
        periodic=is_periodic,
        symbols=tuple(symbols))
Exemplo n.º 8
0
def load_molecule_cpmd(fn_out,
                       fn_geometry,
                       fn_hessian,
                       multiplicity=1,
                       is_periodic=True):
    """Load a molecule with the Hessian from a CPMD computation

       Arguments:
        | fn_out  --  The filename of the output containing the total energy.
        | fn_geometry   --  The filename of geometry and the gradient of the
                            (partially) optimized system. (This filename is
                            typically GEOMETRY.xyz.)
        | fn_hessian  --  The filename of the of the file containing the
                          Hessian. (This filename is typically MOLVIB.)

       Optional arguments:
        | multiplicity  --  The spin multiplicity of the electronic system
                            [default=1]
        | is_periodic  --  True when the system is periodic in three dimensions.
                           False when the system is aperiodic. [default=True]
    """
    # go through the output file: grep the total energy
    energy = None
    f = file(fn_out)
    while True:
        line = f.readline()
        if line == "":
            raise IOError(
                "Could not find final results in %s. Is the output file truncated?"
                % fn_out)
        if line == " *                        FINAL RESULTS                         *\n":
            break
    while True:
        line = f.readline()
        if line == "":
            raise IOError(
                "Could not find total energy in %s. Is the output file truncated?"
                % fn_out)
        if line.startswith(" (K+E1+L+N+X)           TOTAL ENERGY ="):
            words = (line.strip()).split()
            energy = float(words[4])
            break
    f.close()

    # load the optimal geometry
    f = file(fn_geometry)
    num_atoms = int(f.readline())
    numbers = np.zeros(num_atoms, int)
    coordinates = np.zeros((num_atoms, 3), float)
    gradient = np.zeros((num_atoms, 3), float)

    f.readline()
    i = 0
    while True:
        line = f.readline()
        if line == "":
            break  # end of file
        words = (line.strip()).split()
        if len(words) == 7:
            numbers[i] = periodic[words[0]].number
            coordinates[i][0] = float(words[1]) * angstrom
            coordinates[i][1] = float(words[2]) * angstrom
            coordinates[i][2] = float(words[3]) * angstrom
            gradient[i][1] = float(words[4])
            gradient[i][1] = float(words[5])
            gradient[i][2] = float(words[6])
            i += 1
        else:
            raise IOError("Expecting seven words at each atom line in %s." %
                          fn_geometry)
    if i != num_atoms:
        raise IOError("The number of atoms is incorrect in %s." % fn_geometry)
    f.close()

    # go trhough the freq file: hessian
    f = file(fn_hessian)

    line = f.readline()
    if not line.startswith(" &CART"):
        raise IOError("File %s does not start with &CART." % fn_hessian)
    masses = np.zeros(num_atoms, float)
    for i in xrange(num_atoms):
        line = f.readline()
        words = line.split()
        masses[i] = float(words[4]) * amu
    f.readline()  # &END

    line = f.readline()
    if not line.startswith(" &FCON"):
        raise IOError("File %s does not contain section &FCON." % fn_hessian)
    num_cart = num_atoms * 3
    hessian = np.zeros((num_cart, num_cart), float)
    for i in xrange(num_cart):
        line = f.readline()
        words = line.split()
        for j in xrange(num_cart):
            hessian[i, j] = float(words[j])

    f.close()

    return Molecule(numbers, coordinates, masses, energy, gradient, hessian,
                    multiplicity, 0, is_periodic)
Exemplo n.º 9
0
def load_molecule_molpro(filename):
    """Load a molecule from Molpro 2012 output file.

       Argument:
         | ``filename`` -- the molpro 2012 output

    """

    lines = open(filename,"r").read().splitlines()
    # locate positions
    beginxyz = None
    begincoordinate = None
    beginmass = None
    beginhessian = None
    begingradient = None
    for lineno, line in enumerate(lines):
        if "Current geometry" in line:
            beginxyz = lineno + 2
        if "FREQUENCIES * CALCULATION OF NORMAL MODES" in line:
            begincoordinate = lineno + 7
        if "Atomic Masses" in line:
            beginmass = lineno
        if "Force Constants" in line:
            beginhessian = lineno
        # if "GRADIENT FOR" in line:
            # begingradient = lineno + 4
    # xyz read, only meta data and energy
    atomnumber = int(lines[beginxyz].split()[0])
    title = lines[beginxyz+1].split()[0]
    energy = float(lines[beginxyz+1].split("=")[1])
    # print(atomnumber, title, energy)

    # coordinate read
    numbers=[]
    coordinates=[]

    for line in lines[begincoordinate : begincoordinate+atomnumber]:
        words = line.split()
        charge = int(float(words[2]))
        x = float(words[3])
        y = float(words[4])
        z = float(words[5])
        numbers.append(charge)
        coordinates.append([x, y, z])
        # print(charge, x, y, z)

    # masses
    masses = []
    for line in lines[beginmass+1:]:
        if len(line) > 8:
            words = line[8:].split()
            for word in words:
                masses.append(float(word)*amu)
        else:
            break
    # print(masses)

    # gradient
    gradient = np.zeros((atomnumber, 3))
    # TODO, Gradient is more difficult than I thought...
    # format of gradient from CCSD(T)-F12 is different from DFT
    '''
    if begingradient != None:
        for i, line in enumerate(lines[begingradient: begingradient + atomnumber]):
            words = line.split()
            gradient[i,0] = float(words[1])
            gradient[i,1] = float(words[2])
            gradient[i,2] = float(words[3])
    '''
    # print(gradient)

    # hessian
    hessian = np.ndarray((3*atomnumber, 3*atomnumber), dtype= float)
    headerline = beginhessian+1
    rowbegin = 0
    while rowbegin < 3*atomnumber:
        for r, line in zip(\
            range(rowbegin, 3*atomnumber),\
            lines[headerline+1 : headerline+1+3*atomnumber-rowbegin]\
            ):
            words = line[16:].split()
            # print(words)
            for c in range(rowbegin, rowbegin+min(5, 3*atomnumber-rowbegin, len(words))):
                # print(r,c)
                hessian[r,c] = float(words[c-rowbegin])
                hessian[c,r]=hessian[r,c]
        headerline += 3*atomnumber-rowbegin+1
        rowbegin += 5

    # print(hessian)

    return Molecule(
        np.array(numbers),
        np.array(coordinates),
        np.array(masses),
        energy,
        gradient,
        hessian,
        title=title,
        multiplicity=1,
    )
Exemplo n.º 10
0
def create_enm_molecule(molecule,
                        selected=None,
                        numbers=None,
                        masses=None,
                        rcut=8.0 * angstrom,
                        K=1.0,
                        periodic=None):
    """Create a molecule according to the Elastic Network Model

       Argument:
         | molecule  --  The molecule to start from. can be two types: (i) a
                       Molecule object or (ii) a numpy array with shape (N,3)
                       with coordinates in atomic units.

       When a Molecule object is given, atom numbers, masses and periodic are
       inherited from the molecule, unless they are specified explicitly in the
       optional arguments.

       Optional arguments:
         | selected  --  Selection of atoms to include in the ENM model. This can
                         be a list or array of atom indices (length <= N), or an
                         array of booleans (length = N).
         | numbers  --  atom numbers in the ENM model (length = N). default is
                        array of ones or the numbers from the molecule object.
         | masses  --  atomic masses in atomic units in the ENM model (length = N).
                       default is array of hydrogen masses or the masses from the
                       molecule object.
         | rcut  --  cutoff distance between interacting pairs in atomic units
         | K  --  strength of the interaction in atomic units (Hartree/Bohr**2).
                  The interaction strength is the same for all interacting pairs.
    """
    if isinstance(molecule, Molecule):
        coordinates = molecule.coordinates
        if numbers is None:
            numbers = molecule.numbers
        if masses is None:
            masses = molecule.masses
        if periodic is None:
            periodic = molecule.periodic
    else:
        coordinates = np.array(molecule, copy=False)
        if numbers is None:
            # pretend being hydrogens
            numbers = np.ones(len(coordinates))
        if masses is None:
            # pretend being hydrogens
            masses = np.ones(len(coordinates), float) * amu
        if periodic is None:
            periodic = False

    if selected is not None:
        coordinates = coordinates[selected]
        numbers = numbers[selected]
        masses = masses[selected]

    rcut2 = rcut**2
    N = len(coordinates)
    hessian = np.zeros((3 * N, 3 * N), float)
    for i in range(N):
        for j in range(i + 1, N):
            x = np.reshape((coordinates[i, :] - coordinates[j, :]), (3, 1))
            dist2 = np.sum(x**2)
            if dist2 < rcut2:
                corr = K * np.dot(x, x.transpose()) / dist2
                hessian[3 * i:3 * (i + 1), 3 * i:3 * (i + 1)] += corr
                hessian[3 * i:3 * (i + 1), 3 * j:3 * (j + 1)] -= corr
                hessian[3 * j:3 * (j + 1), 3 * i:3 * (i + 1)] -= corr
                hessian[3 * j:3 * (j + 1), 3 * j:3 * (j + 1)] += corr

    return Molecule(
        numbers,
        coordinates,
        masses,
        0.0,  #energy
        np.zeros((len(coordinates), 3), float),  #gradient
        hessian,
        1,  # multiplicity
        1,  # rotational symmetry number
        periodic,
    )