Exemplo n.º 1
0
def g09_results(NEB, step_to_use, i, state):
    '''
    A method for reading in the output of Gaussian09 single point calculations
    for NEB calculations. This will both (a) assign forces to the atoms stored
    in state and (b) return the energy and atoms.

    **Parameters**

        NEB: :class:`NEB`
            An NEB container holding the main NEB simulation
        step_to_use: *int*
            Which iteration in the NEB sequence the output to be read in is on.
        i: *int*
            The index corresponding to which image on the frame is to be
            simulated.
        state: *list,* :class:`squid.structures.atom.Atom`
            A list of atoms describing the image on the frame associated with
            index *i*.

    **Returns**

        new_energy: *float*
            The energy of the system in Hartree (Ha).
        new_atoms: *list,* :class:`squid.structures.atom.Atom`
            A list of atoms with the forces attached in units of Hartree per
            Angstrom (Ha/Ang).
    '''
    result = g09.parse_atoms('%s-%d-%d' % (NEB.name, step_to_use, i),
                             check_convergence=False,
                             parse_all=False)
    if not result:
        raise Exception('parse_atoms failed')
    new_energy, new_atoms = result

    # Check if coordinates are aligned properly between state and new_atoms
    def check_atom_coords(atoms1, atoms2, precision=1e-6):
        for a1, a2 in zip(atoms1, atoms2):
            if (abs(a1.x - a2.x) > precision or
                abs(a1.y - a2.y) > precision or
                    abs(a1.z - a2.z) > precision):
                print(i, 'atoms not in same frame:', a1.x, a1.y, a1.z,)
                print('vs', a2.x, a2.y, a2.z)
                print(abs(a1.x - a2.x), abs(a1.y - a2.y), abs(a1.z - a2.z))
                exit()

    if i != 0 and i != len(NEB.states) - 1:
        check_atom_coords(state, new_atoms)
        for a, b in zip(state, new_atoms):
            b.fx = units.convert('Ha/Bohr', 'Ha/Ang', b.fx)
            b.fy = units.convert('Ha/Bohr', 'Ha/Ang', b.fy)
            b.fz = units.convert('Ha/Bohr', 'Ha/Ang', b.fz)
            a.fx = b.fx
            a.fy = b.fy
            a.fz = b.fz

    return new_energy, new_atoms
Exemplo n.º 2
0
 def f(x):
     return units.convert("Ha/Ang", "eV/Ang", x)
Exemplo n.º 3
0
def read(input_file, atom_units="Ang"):
    '''
    General read in of all possible data from a JDFTx output file.

    **Parameters**

        input_file: *str*
            JDFTx output file to be parsed.
        atom_units: *str, optional*
            What units you want coordinates to be converted to.

    **Returns**

        data: :class:`results.DFT_out`
            Generic DFT output object containing all parsed results.

    '''
    raise Exception("NEEDS TO BE DONE!")
    # Check file exists, and open
    # Allow absolute paths as filenames
    if not input_file.startswith('/') and not os.path.isfile(input_file):
        input_path = 'jdftx/%s/%s.out' % (input_file, input_file)
    else:
        input_path = input_file
    if not os.path.isfile(input_path):
        raise IOError('Expected JDFTx output file does not exist at %s'
                      % (input_path))
        sys.exit()
    data = open(input_path, 'r').read()
    data_lines = data.splitlines()

    # Get coordinates
    section, frames, gradients = data, [], []
    s = "# Ionic positions in cartesian coordinates:"
    ss = "# Forces in Cartesian coordinates:"
    while s in section:
        section = section[section.find(s) + len(s):]
        atom_block = section[:section.find('\n\n')].split('\n')[1:]
        frame, gradient = [], []
        for i, line in enumerate(atom_block):
            a = line.split()
            frame.append(structures.Atom(
                a[1],
                units.convert_dist("Bohr", atom_units, float(a[2])),
                units.convert_dist("Bohr", atom_units, float(a[3])),
                units.convert_dist("Bohr", atom_units, float(a[4])),
                index=i))
        frames.append(frame)

        # If we also have forces, read those in
        if ss in section:
            section = section[section.find(ss) + len(ss):]
            force_block = section[:section.find('\n\n')].split('\n')[1:]
            for i, line in enumerate(force_block):
                a = line.split()
                frames[-1][i].fx = units.convert(
                    "Ha/Bohr", "Ha/%s" % atom_units, float(a[2]))
                frames[-1][i].fy = units.convert(
                    "Ha/Bohr", "Ha/%s" % atom_units, float(a[3]))
                frames[-1][i].fz = units.convert(
                    "Ha/Bohr", "Ha/%s" % atom_units, float(a[4]))
                gradient.append(
                    [frames[-1][i].fx, frames[-1][i].fy, frames[-1][i].fz])
            gradients.append(gradient)

    atoms = None
    if frames:
        atoms = frames[-1]

    section, energies = data, []
    s = "IonicMinimize: Iter:"
    while s in section:
        section = section[section.find(s) + len(s):]
        energy = float(section.split("\n")[0].strip().split()[2])
        grad_k = float(section.split("\n")[0].strip().split()[4])
        energies.append(energy)
    convergence = None
    if len(energies) > 2:
        section = data[data.find("ionic-minimize"):]
        de_criteria = float(section[
            section.find("energyDiffThreshold"):].split("\n")[0].strip().split()[1])
        k_criteria = float(section[
            section.find("knormThreshold"):].split("\n")[0].strip().split()[1])
        de1 = abs(energies[-2] - energies[-3])
        de2 = abs(energies[-1] - energies[-2])
        convergence = [
            ["Change in Energy 1",
             "%.2e" % de1,
             de_criteria,
             ["NO", "YES"][de_criteria > de1]],
            ["Change in Energy 2",
             "%.2e" % de2,
             de_criteria,
             ["NO", "YES"][de_criteria > de2]],
            ["K Norm",
             "%.2e" % abs(grad_k),
             k_criteria,
             ["NO", "YES"][k_criteria > abs(grad_k)]]
        ]

    energy = None
    if energies:
        energy = energies[-1]

    converged = None
    finished = "Done!" in data
    if "IonicMinimize: Converged" in data:
        converged = True
    elif finished:
        converged = False

    time = None
    if "Duration:" in data:
        time = data[data.find("Duration:"):].split("\n")[0].split("Duration:")[-1].strip()[:-1].split(":")
        # Time should be x-x:yy:zz.zz, thus: [x-x, yy, zz.zz]
        time = float(time[2]) + 60.0 * float(time[1]) + 3600.0 * float(time[0].split("-")[-1])

    data = results.DFT_out(input_file, 'jdftx')

    # data.route = route
    # data.extra_section = extra_section
    # data.charge_and_multiplicity = charge_and_multiplicity.strip()
    data.frames = frames
    data.atoms = atoms
    data.gradients = gradients
    data.energies = energies
    data.energy = energy
    # data.charges_MULLIKEN = charges_MULLIKEN
    # data.charges_LOEWDIN = charges_LOEWDIN
    # data.charges_CHELPG = charges_CHELPG
    # data.charges = copy.deepcopy(charges_MULLIKEN)
    # data.MBO = MBO
    data.convergence = convergence
    data.converged = converged
    data.time = time
    # data.bandgaps = bandgaps
    # data.bandgap = bandgap
    # data.orbitals = orbitals
    data.finished = finished
    # data.warnings = warnings

    return data
Exemplo n.º 4
0
    def calculate(self, coords):
        self.calls_to_calculate += 1

        # Update coordinates in states. This won't change anything on
        # the first run through, but will on subsequent ones
        coord_count = 0
        for s in self.states[1:-1]:
            for a in s:
                a.x, a.y, a.z = coords[coord_count:coord_count + 3]
                coord_count += 3

        # Start DFT jobs
        running_jobs = []
        if self.initialize == True:
        # Run a single point calculation to determine energies before main curve-smoothing begins
            for i, state in enumerate(self.states):
                running_jobs.append(
                    self.start_job(self,
                                   i,
                                   state,
                                   self.charge,
                                   self.procs,
                                   self.queue,
                                   self.initial_guess,
                                   self.extra_section,
                                   self.mem,
                                   self.priority)
                )
        else:
        # Once initialization is complete, run main spline_NEB simulation for curve smoothing
            for i, state in enumerate(self.states):
                if (i == 0 or i == self.peak or i == len(self.states) - 1) and self.step > 0:
                    # No need to calculate anything for first and last states
                    # after the first step
                    pass
                else:
                    running_jobs.append(
                        self.start_job(self,
                                       i,
                                       state,
                                       self.charge,
                                       self.procs,
                                       self.queue,
                                       self.initial_guess,
                                       self.extra_section,
                                       self.mem,
                                       self.priority)
                    )
        # Wait for jobs to finish
        for j in running_jobs:
            j.wait()

        # Get forces and energies from DFT calculations
        energies = []
        for i, state in enumerate(self.states):
            # State 0 and state N-1 don't change, so just use result
            # from self.step == 0
            if (i == 0 or i == self.peak or i == len(self.states) - 1):
                step_to_use = 0
            else:
                step_to_use = self.step

            new_energy, new_atoms = self.get_results(
                self, step_to_use, i, state
            )
            energies.append(new_energy)

        # V = potential energy from DFT. energies = V+springs
        V = copy.deepcopy(energies)

        # Get positions in a flat array
        def get_positions(image):
            pos = np.array([np.empty([3]) for j in image])
            for j, atom in enumerate(image):
                if j not in self.spring_atoms:
                    continue
                pos[j] = np.array([atom.x, atom.y, atom.z])
            return pos.flatten()

        if self.initialize == True:
        # During initialization phase, use single point energies previously calculated to
        # determine highest energy frame (peak) and generate spring constants between frames 
            # Peak of reaction coordinate is highest energy frame
            self.peak = energies.index(max(energies))

            #Calculate spring constants for smoothing curve
            d_before = np.linalg.norm(get_positions(self.states[self.peak]) - 
                                      get_positions(self.states[0]))
            d_after = np.linalg.norm(get_positions(self.states[self.peak]) - 
                                     get_positions(self.states[-1]))

            l_before = - d_before ** 2 / np.log(self.gamma)
            l_after = - d_after ** 2 / np.log(self.gamma)

            x1, x2 = [], []    
            for i in range(self.peak):
                v = (get_positions(self.states[i]) + 
                     get_positions(self.states[i+1])) / 2.0 - \
                     get_positions(self.states[0])
                x1.append(np.linalg.norm(v))

            for i in range(self.peak, len(self.states) - 1):
                v = (get_positions(self.states[i]) + 
                     get_positions(self.states[i+1])) / 2.0 - \
                     get_positions(self.states[0])
                x2.append(np.linalg.norm(v))

            for x in x1:
                self.k.append(self.k_max * exp(-((x - d_before) ** 2) / l_before))
            for x in x2:
                self.k.append(self.k_max * exp(-((x - d_after) ** 2) / l_after))

        # Add spring forces to atoms
        for i in range(1, len(self.states) - 1):
            if i == self.peak:
                # Set NEB forces at peak to 0
                for j, atom in enumerate(self.states[i]):
                    if j not in self.spring_atoms:
                        continue
                    atom.fx, atom.fy, atom.fz = [0, 0, 0]
            else:
                a = get_positions(self.states[i - 1])
                b = get_positions(self.states[i])
                c = get_positions(self.states[i + 1])

                real_force = np.array([np.empty([3]) for j in self.states[i]])
                for j, atom in enumerate(self.states[i]):
                    if j not in self.spring_atoms:
                        continue
                    real_force[j] = np.array([atom.fx, atom.fy, atom.fz])
                real_force = real_force.flatten()

                # Find tangent
                tplus = c - b
                tminus = b - a
                dVmin = min(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i]))
                dVmax = max(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i]))

                if V[i + 1] > V[i] and V[i] > V[i - 1]:
                    tangent = tplus.copy()
                elif V[i + 1] < V[i] and V[i] < V[i - 1]:
                    tangent = tminus.copy()
                elif V[i + 1] > V[i - 1]:
                    tangent = tplus * dVmax + tminus * dVmin
                else:
                    tangent = tplus * dVmin + tminus * dVmax

                # Normalize tangent
                tangent_norm = np.sqrt(np.vdot(tangent, tangent))
                if tangent_norm != 0:
                    tangent /= tangent_norm
		
		# Set NEB forces
                forces = (self.k[i] * np.linalg.norm(tplus) -
                                     self.k[i-1] * np.linalg.norm(tminus)) * tangent
                forces = forces.reshape((-1, 3))
                for j, atom in enumerate(self.states[i]):
                    if j not in self.spring_atoms:
                        continue
                    atom.fx, atom.fy, atom.fz = forces[j]

        # Remove net translation forces from the gradient
        if self.fit_rigid:
            net_translation_force = []
            for state in self.states[1:-1]:
                net_force = np.zeros(3)
                for a in state:
                    net_force += (a.fx, a.fy, a.fz)
                net_trans = np.sqrt((net_force**2).sum()) / len(state)
                net_translation_force.append(net_trans)
                for a in state:
                    a.fx -= net_force[0] / len(state)
                    a.fy -= net_force[1] / len(state)
                    a.fz -= net_force[2] / len(state)
            max_translation_force = units.convert(
                "Ha/Ang",
                "eV/Ang",
                max(net_translation_force)
            )
        else:
            max_translation_force = 0

        # Set gradient
        self.gradient = []
        for state in self.states[1:-1]:
            for a in state:
                # Gradient of self.error
                self.gradient += [-a.fx, -a.fy, -a.fz]

        # Calculate RMS Force and Max force
        force_mags = [(a.fx**2 + a.fy**2 + a.fz**2)**0.5
                      for state in self.states[1:-1] for a in state]
        RMS_force = geometry.rms(force_mags)
        self.RMS_force = RMS_force
        MAX_force = max(force_mags)
        self.MAX_force = MAX_force

        # Print data
        V = V[:1] + [units.convert_energy("Ha", "kT_300", e - V[0])
                     for e in V[1:]]
        MAX_energy = max(V)
        if self.prv_RMS is None or self.prv_RMS > RMS_force:
            rms = print_helper.color_set(
                float("%.4f" % units.convert_energy(
                    "Ha", "eV", RMS_force)
                ), 'GREEN')
        else:
            rms = print_helper.color_set(
                float("%.4f" % units.convert_energy(
                    "Ha", "eV", RMS_force)
                ), 'RED')
        if self.prv_MAX is None or self.prv_MAX > MAX_force:
            max_f = print_helper.color_set(
                float("%.4f" % units.convert_energy(
                    "Ha", "eV", MAX_force)
                ), 'GREEN')
        else:
            max_f = print_helper.color_set(
                float("%.4f" % units.convert_energy(
                    "Ha", "eV", MAX_force)
                ), 'RED')
        if self.prv_MAX_E is None or self.prv_MAX_E > MAX_energy:
            max_e = print_helper.color_set(
                float("%.1f" % MAX_energy), 'GREEN')
        else:
            max_e = print_helper.color_set(
                float("%.1f" % MAX_energy), 'RED')
        if self.step == 0 and self.initialize == False:
            print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\tMAX_E (kT_300)\
\tMAX Translational Force (eV/Ang)\tEnergies (kT_300)\n----")
        print("%d\t%s\t\t%s\t\t%s\t\t%.4f"
              % (self.step, rms, max_f, max_e, max_translation_force)),

        print('    \t\t\t\t', '%7.5g +'\
              % V[0], ('%5.1f ' * len(V[1:])) % tuple(V[1:]))

        sys.stdout.flush()

        if self.prv_RMS is None:
            self.prv_RMS = RMS_force
        self.prv_RMS = min(RMS_force, self.prv_RMS)

        if self.prv_MAX is None:
            self.prv_MAX = MAX_force
        self.prv_MAX = min(MAX_force, self.prv_MAX)

        if self.prv_MAX_E is None:
            self.prv_MAX_E = MAX_energy
        self.prv_MAX_E = min(MAX_energy, self.prv_MAX_E)

        # Set error
        self.error = RMS_force

        # Increment step
        self.step += 1

        # End initialization phase
        self.initialize = False

        if self.callback is not None:
            self.callback(self.states)
Exemplo n.º 5
0
    def calculate(self, coords):
        self.calls_to_calculate += 1

        # Update coordinates in states. This won't change anything on
        # the first run through, but will on subsequent ones
        coord_count = 0
        for s in self.states[1:-1]:
            for a in s:
                a.x, a.y, a.z = coords[coord_count:coord_count + 3]
                coord_count += 3

        # Start DFT jobs
        running_jobs = []
        for i, state in enumerate(self.states):
            if (i == 0 or i == len(self.states) - 1) and self.step > 0:
                # No need to calculate anything for first and last states
                # after the first step
                pass
            else:
                running_jobs.append(
                    self.start_job(self, i, state, self.charge, self.procs,
                                   self.queue, self.initial_guess,
                                   self.extra_section, self.mem,
                                   self.priority))
        # Wait for jobs to finish
        for j in running_jobs:
            j.wait()

        # Get forces and energies from DFT calculations
        energies = []
        for i, state in enumerate(self.states):
            # State 0 and state N-1 don't change, so just use result
            # from self.step == 0
            if (i == 0 or i == len(self.states) - 1):
                step_to_use = 0
            else:
                step_to_use = self.step

            new_energy, new_atoms = self.get_results(self, step_to_use, i,
                                                     state)
            energies.append(new_energy)

        # V = potential energy from DFT. energies = V+springs
        V = copy.deepcopy(energies)

        # In climbing image ANEB, after a few iterations we take the highest
        # energy image and use that.
        if self.ci_ANEB and self.ci_img is None and self.step > self.ci_N:
            self.ci_img = V.index(max(V))
            if self.ci_img in [0, len(self.states) - 1]:
                raise Exception("CI found endpoint. Is your band correct?")

        # Get positions in a flat array
        def get_positions(image):
            pos = np.array([np.empty([3]) for j in image])
            for j, atom in enumerate(image):
                if j not in self.spring_atoms:
                    continue
                pos[j] = np.array([atom.x, atom.y, atom.z])
            return pos.flatten()

        # Add spring forces to atoms
        for i in range(1, len(self.states) - 1):
            a = get_positions(self.states[i - 1])
            b = get_positions(self.states[i])
            c = get_positions(self.states[i + 1])

            real_force = np.array([np.empty([3]) for j in self.states[i]])
            for j, atom in enumerate(self.states[i]):
                if j not in self.spring_atoms:
                    continue
                real_force[j] = np.array([atom.fx, atom.fy, atom.fz])
            real_force = real_force.flatten()

            # Find tangent
            tplus = c - b
            tminus = b - a
            dVmin = min(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i]))
            dVmax = max(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i]))

            if V[i + 1] > V[i] and V[i] > V[i - 1]:
                tangent = tplus.copy()
            elif V[i + 1] < V[i] and V[i] < V[i - 1]:
                tangent = tminus.copy()
            elif V[i + 1] > V[i - 1]:
                tangent = tplus * dVmax + tminus * dVmin
            else:
                tangent = tplus * dVmin + tminus * dVmax

            # Normalize tangent
            tangent_norm = np.sqrt(np.vdot(tangent, tangent))
            if tangent_norm != 0:
                tangent /= tangent_norm

            F_spring_parallel = self.k[i - 1] * (
                np.linalg.norm(tplus) - np.linalg.norm(tminus)) * tangent

            F_real_perpendicular = real_force -\
                (np.vdot(real_force, tangent) * tangent)

            # Set ANEB forces
            # Note, in climbing image we have the formula:
            #    F = F_real - 2*F_real*tau*tau
            # Vs the normal:
            #    F = F_spring_parallel + F_real_perpendicular
            if self.ci_img is not None and i == self.ci_img:
                forces = real_force - 2.0 * np.vdot(real_force,
                                                    tangent) * tangent
            else:
                forces = F_spring_parallel + F_real_perpendicular
            forces = forces.reshape((-1, 3))
            for j, atom in enumerate(self.states[i]):
                if j not in self.spring_atoms:
                    continue
                atom.fx, atom.fy, atom.fz = forces[j]

        # Remove net translation forces from the gradient
        if self.fit_rigid:
            net_translation_force = []
            for state in self.states[1:-1]:
                net_force = np.zeros(3)
                for a in state:
                    net_force += (a.fx, a.fy, a.fz)
                net_trans = np.sqrt((net_force**2).sum()) / len(state)
                net_translation_force.append(net_trans)
                for a in state:
                    a.fx -= net_force[0] / len(state)
                    a.fy -= net_force[1] / len(state)
                    a.fz -= net_force[2] / len(state)
            max_translation_force = units.convert("Ha/Ang", "eV/Ang",
                                                  max(net_translation_force))
        else:
            max_translation_force = 0

        # Set gradient
        self.gradient = []
        for state in self.states[1:-1]:
            for a in state:
                # Gradient of self.error
                self.gradient += [-a.fx, -a.fy, -a.fz]

        # Calculate RMS Force and Max force
        force_mags = [(a.fx**2 + a.fy**2 + a.fz**2)**0.5
                      for state in self.states[1:-1] for a in state]
        RMS_force = geometry.rms(force_mags)
        self.RMS_force = RMS_force
        MAX_force = max(force_mags)
        self.MAX_force = MAX_force

        # Print data
        V = V[:1] + [
            units.convert_energy("Ha", "kT_300", e - V[0]) for e in V[1:]
        ]
        MAX_energy = max(V)
        if self.prv_RMS is None or self.prv_RMS > RMS_force:
            rms = print_helper.color_set(
                float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)),
                'GREEN')
        else:
            rms = print_helper.color_set(
                float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)),
                'RED')
        if self.prv_MAX is None or self.prv_MAX > MAX_force:
            max_f = print_helper.color_set(
                float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)),
                'GREEN')
        else:
            max_f = print_helper.color_set(
                float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)),
                'RED')
        if self.prv_MAX_E is None or self.prv_MAX_E > MAX_energy:
            max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'GREEN')
        else:
            max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'RED')
        if self.step == 0:
            print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\tMAX_E (kT_300)\
\tMAX Translational Force (eV/Ang)\tEnergies (kT_300)\n----")
        print(
            "%d\t%s\t\t%s\t\t%s\t\t%.4f" %
            (self.step, rms, max_f, max_e, max_translation_force) +
            '\t\t\t\t%7.5g +' % V[0], ('%5.1f ' * len(V[1:])) % tuple(V[1:]))

        self.energy_gaps = [V[1]] + [y - x for y, x in zip(V[2:], V[1:-1])]

        sys.stdout.flush()

        if self.prv_RMS is None:
            self.prv_RMS = RMS_force
        self.prv_RMS = min(RMS_force, self.prv_RMS)

        if self.prv_MAX is None:
            self.prv_MAX = MAX_force
        self.prv_MAX = min(MAX_force, self.prv_MAX)

        if self.prv_MAX_E is None:
            self.prv_MAX_E = MAX_energy
        self.prv_MAX_E = min(MAX_energy, self.prv_MAX_E)

        # Set error
        self.error = RMS_force

        # Increment step
        self.step += 1

        if self.callback is not None:
            self.callback(self.states)
Exemplo n.º 6
0
def read(input_file):
    '''
    General read in of all possible data from an Orca output file (.out).
    It should be mentioned that atomic positions are 0 indexed.

    **Parameters**

        input_file: *str*
            Orca .out file to be parsed.

    **Returns**

        data: :class:`squid.structures.results.DFT_out`
            Generic DFT output object containing all parsed results.
    '''
    # Check file exists, and open
    # Allow absolute paths as filenames
    if input_file.startswith('/'):
        input_path = input_file
    elif os.path.isfile(input_file):
        input_path = input_file
    else:
        input_path = 'orca/%s/%s.out' % (input_file, input_file)
    if not os.path.isfile(input_path):
        raise IOError('Expected orca output file does not exist at %s'
                      % (input_path))
    data = open(input_path, 'r').read()
    data_lines = data.splitlines()

    # Get the route line
    try:
        route = [line[5:] for line in data_lines
                 if line.startswith('|  1>')][0].strip()
    except IndexError:
        raise IOError('Could not find route line in %s: \
job most likely crashed.' % input_path)

    # Get the extra section
    es_block, skip_flag = [], True
    for d in data_lines:
        line = d.strip()
        if line.startswith('|  1>'):
            skip_flag = False
        if skip_flag:
            continue
        if "*xyz" in line:
            charge_and_multiplicity = line.split("xyz")[-1]
            break
        line = line.split(">")[-1].strip()
        if line.startswith("!"):
            continue
        es_block.append(line)
    if es_block == []:
        extra_section = ""
    else:
        extra_section = "\n".join(es_block)

    # Get all the energies
    energies = re.findall('FINAL SINGLE POINT ENERGY +(\S+)', data)
    energies = [float(e) for e in energies]

    if len(energies) > 0:
        energy = min(energies)
    else:
        energy = None

    # Get all the positions
    section, frames = data, []
    s = 'CARTESIAN COORDINATES (ANGSTROEM)'
    while s in section:
        section = section[section.find(s) + len(s):]
        atom_block = section[:section.find('\n\n')].split('\n')[2:]
        frame = []
        for i, line in enumerate(atom_block):
            a = line.split()
            frame.append(Atom(
                a[0],
                float(a[1]),
                float(a[2]),
                float(a[3]),
                index=i)
            )
        frames.append(frame)

    if frames:
        atoms = frames[-1]
    else:
        atoms = None

    # Get all the gradients if CARTESIAN GRADIENTS is in the file.
    # Else, if MP2 gradients is in the file, grab the last gradient
    s_gradient = "CARTESIAN GRADIENT"
    s_gradient_2 = "The final MP2 gradient"
    section, gradients = data, []
    if s_gradient in section:
        s = s_gradient
    elif s_gradient_2 in section:
        s = s_gradient_2
    else:
        s, gradients = None, None
    if s is not None:
        while s in section:
            gradient = []
            if s == s_gradient:
                grad_block = section[section.find(s_gradient):]
                grad_block = grad_block.split("\n\n")[1].split("\n")
                grad_block = [g for g in grad_block if "WARNING" not in g]
                gradient = []
                for line in grad_block:
                    a = line.split()
                    gradient.append([float(b) for b in a[3:]])
            elif s == s_gradient_2:
                grad_block = section[section.find(s_gradient_2):]
                grad_block = grad_block.split("\n\n")[0].split("\n")[1:]
                gradient = []
                for line in grad_block:
                    a = line.split()
                    gradient.append([float(b) for b in a[1:]])
            section = section[section.find(s) + len(s):]
            gradients.append(gradient)

    # Get charges
    hold, charges_MULLIKEN = data, []
    s = 'MULLIKEN ATOMIC CHARGES'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s):]
        b = hold[:hold.find('\n\n')].split('\n')[2:-1]
        for a in b:
            a = a.split()
            charges_MULLIKEN.append([a[1].split(':')[0], float(a[-1])])
    else:
        charges_MULLIKEN = None

    hold, charges_LOEWDIN = data, []
    s = 'LOEWDIN ATOMIC CHARGES'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s):]
        b = hold[:hold.find('\n\n')].split('\n')[2:]
        for a in b:
            a = a.split()
            charges_LOEWDIN.append([a[1].split(':')[0], float(a[-1])])
        for a, charge in zip(atoms, charges_LOEWDIN):
            a.charge = charge[1]
    else:
        charges_LOEWDIN = None

    hold, charges_CHELPG = data, []
    s = 'CHELPG Charges'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s):]
        s_id = '\n--------------------------------\nTotal charge:'
        b = hold[:hold.find(s_id)].split('\n')[2:]
        for a in b:
            a = a.split()
            charges_CHELPG.append([a[1].split(':')[0], float(a[-1])])
        for a, charge in zip(atoms, charges_CHELPG):
            a.charge = charge[1]
    else:
        charges_CHELPG = None

    # Get Mayer Bond Orders
    hold, MBO = data, []
    s = 'Mayer bond orders larger than 0.1'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s):]
        b = hold[:hold.find('\n\n')].split('\n')[1:]
        b = " ".join(b).split("B(")
        while len(b) > 0 and b[0].strip() == "":
            b = b[1:]
        while len(b) > 0 and b[-1].strip() == "":
            b = b[:-1]
        for a in b:
            a = a.split(":")
            mbo_x = float(a[-1])
            bond_x = [int(c.strip().split("-")[0]) for c in a[0].split(",")]
            MBO.append([[atoms[x] for x in bond_x], mbo_x])

    # Get Total Simulation Time
    hold = data
    s = 'TOTAL RUN TIME'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s):]
        hold = hold[:hold.find('\n')].split()
        time = float(hold[3]) * 3600 * 24 + \
            float(hold[5]) * 3600 + \
            float(hold[7]) * 60 + \
            float(hold[9]) + \
            float(hold[11]) / 1000.0
    else:
        time = float('NaN')

    hold, bandgaps = data, []
    s = 'ORBITAL ENERGIES'
    while hold.find(s) != -1:
        hold = hold[hold.find(s) + len(s):]
        tmp = hold[:hold.replace('\n\n', '\t\t', 1).find('\n\n')]
        tmp = tmp.split('\n')[4:]
        tp = None
        for i, t in enumerate(tmp):
            t = t.split()
            if float(t[1]) == 0:
                if i == 0:
                    raise Exception("Error in calculating bandgaps. \
Lowest energy orbital is empty.")
                bandgaps.append(float(t[2]) - float(tp[2]))
                break
            tp = t
        hold = hold[hold.find('\n'):]

    if len(bandgaps) > 0:
        bandgap = bandgaps[-1]
    else:
        bandgap = None

    hold, orbitals = data, []
    s = 'ORBITAL ENERGIES'
    hold = '\n'.join(hold[hold.rfind(s):].split('\n')[4:])
    hold = hold[:hold.find("\n\n")].split("\n")
    if hold != ['']:
        orbitals = [(float(h.split()[1]), float(h.split()[2])) for h in hold]
    else:
        orbitals = None

    hold, convergence = data, []
    s = 'Geometry convergence'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s) + len(s):]

        # Cartesian optimization does not compute Max(Bonds).
        # Instead use a more general '\n\n' if 'Max(Bonds)' cannot be found
        if hold.rfind('Max(Bonds)') != -1:
            tmp = hold[:hold.rfind('Max(Bonds)')].split('\n')[3:-2]
        else:
            tmp = hold[:hold.find('\n\n')].split('\n')[3:-1]

        convergence = []
        for a in tmp:
            a = a.split()
            convergence.append([' '.join(a[:2]),
                                float(a[2]),
                                float(a[3]),
                                a[4]])
    else:
        convergence = None

    hold, dipole = data, []
    s = 'DIPOLE MOMENT'
    if hold.rfind(s) != -1:
        hold = hold[hold.rfind(s) + len(s):]
        if hold.rfind('Total Dipole Moment') != -1:
            dipole_moment = hold[hold.rfind('Total Dipole Moment'):].split('\n')[0]
            dipole_moment = tuple([float(x) for x in dipole_moment.strip().split()[-3:]])
        else:
            dipole_moment = None
        if hold.rfind('Magnitude (Debye)') != -1:
            dipole_mag = hold[hold.rfind('Magnitude (Debye)'):].split('\n')[0]
            dipole_mag = float(dipole_mag.strip().split()[-1])
        else:
            dipole_mag = None
        COM = None
        if data.rfind("CENTER OF MASS") != -1:
            COM = data[data.rfind("CENTER OF MASS"):].split("\n")[0].strip().split()[-3:]
            x = float(COM[0].strip()[1:-1])
            y = float(COM[1].strip())
            z = float(COM[2].strip()[:-1])
            COM = tuple(map(
                lambda d: units.convert_dist("Bohr", "Ang", d), (x, y, z)
            ))
        dipole = [dipole_mag, dipole_moment, COM]
    else:
        dipole = None

    hold, converged = data, False
    s1, s2 = 'SCF CONVERGED AFTER', 'OPTIMIZATION RUN DONE'
    if 'opt' in route.lower():
        s = s2
    else:
        s = s1
    if hold.find(s) != -1:
        converged = True

    finished = 'ORCA TERMINATED NORMALLY' in data

    # Read in Vibrational Frequencies if they exist
    s1, s2 = 'VIBRATIONAL FREQUENCIES', 'NORMAL MODES'
    hold, vibfreq = data, None
    if hold.rfind(s1) != -1 and hold.rfind(s2) != -1:
        tmp = hold[hold.rfind(s1):hold.rfind(s2)].strip().split("\n")
        vibfreq = [float(t.split(":")[1].split("cm")[0].strip())
                   for t in tmp if ":" in t]

    warnings = [line for line in data_lines if line.startswith('Warning: ')]

    data = results.DFT_out(input_file, 'orca')

    if isinstance(route, str):
        route = route.strip()
    if isinstance(extra_section, str):
        extra_section = extra_section.strip()
    data.route = route
    data.extra_section = extra_section
    data.charge, data.multiplicity = map(
        float,
        charge_and_multiplicity.strip().split())
    data.frames = frames
    data.atoms = atoms
    data.gradients = gradients  # In default units of Ha/Bohr
    data.forces = None
    # Forces are in Ha/Ang, more "reasonable" units for what we expect when
    # we use forces on the coordinates (in Ang)
    if data.gradients is not None:
        data.forces = -1.0 * np.array(gradients) *\
            units.convert("Ha/Bohr", "Ha/Ang", 1.0)
    data.energies = energies
    data.energy = energy
    data.charges_MULLIKEN = charges_MULLIKEN
    data.charges_LOEWDIN = charges_LOEWDIN
    data.charges_CHELPG = charges_CHELPG
    data.charges = copy.deepcopy(charges_MULLIKEN)
    data.MBO = MBO
    data.vibfreq = vibfreq
    data.convergence = convergence
    data.converged = converged
    data.time = time
    data.bandgaps = bandgaps
    data.bandgap = bandgap
    data.orbitals = orbitals
    data.finished = finished
    data.warnings = warnings
    data.dipole = dipole  # Dipole are in units of Angstrom and/or Debye

    return data
Exemplo n.º 7
0
def engrad_read(input_file, force='Ha/Bohr', pos='Bohr'):
    '''
    General read in of all possible data from an Orca engrad file
    (.orca.engrad).

    **Parameters**

        input_file: *str*
            Orca .orca.engrad file to be parsed.
        force: *str, optional*
            Units you want force to be returned in.  Default is Ha/Bohr.
        pos: *str, optional*
            Units you want position to be returned in. Default is Bohr.

    **Returns**

        atoms: *list,* :class:`squid.structures.atom.Atom`
            A list of the final atomic state, with forces appended
            to each atom.
        energy: *float*
            The total energy of this simulation.
    '''
    if not input_file.endswith('.engrad'):
        input_file = 'orca/%s/%s.orca.engrad' % (input_file, input_file)
    if not os.path.isfile(input_file):
        raise IOError("No engrad file %s exists in %s. \
Please run simulation with grad=True." % (input_file, os.getcwd()))

    data = open(input_file, 'r').read().split('\n')
    count, grad, atoms = 0, [], []
    i = -1
    while i < len(data):
        i += 1
        line = data[i]
        if len(line) < 1:
            continue
        if line.strip()[0] == '#':
            continue
        if count == 0:
            num_atoms = int(line.strip())
            count += 1
        elif count == 1:
            # Get energy
            energy = float(line.strip())
            count += 1
        elif count == 2:
            # Get gradient
            for j in range(num_atoms):
                for k in range(3):
                    grad.append(float(data[i + k].strip()))
                i += 3
            count += 1
        elif count == 3:
            # Get atom coordinates
            k = 0
            for j in range(num_atoms):
                tmp = data[i].split()
                atoms.append(Atom(
                    tmp[0],
                    units.convert_dist('Bohr', pos, float(tmp[1])),
                    units.convert_dist('Bohr', pos, float(tmp[2])),
                    units.convert_dist('Bohr', pos, float(tmp[3]))
                ))
                atoms[-1].fx = units.convert('Ha/Bohr', force, -grad[k])
                atoms[-1].fy = units.convert('Ha/Bohr', force, -grad[k + 1])
                atoms[-1].fz = units.convert('Ha/Bohr', force, -grad[k + 2])
                i += 1
                k += 3
            break

    return atoms, energy