def main(file_name): xyz_file = XYZFile(file_name) frames = xyz_file.geometries # titles = xyz_file.titles # Unit cell, decide how to make this general matrix = np.array([[ 1.4731497044857509E+01, 3.2189795740722255E-02, 4.5577626559295564E-02 ], [ 4.2775481701113616E-02, 2.1087874593411915E+01, -2.8531114198383896E-02 ], [ 6.4054385616337750E-02, 1.3315840416191497E-02, 1.4683043045316882E+01 ]]) matrix *= angstrom cell = UnitCell(matrix) frac = UnitCell.to_fractional(cell, frames) xmin = np.min(frac[:, :, 0]) ymin = np.min(frac[:, :, 1]) zmin = np.min(frac[:, :, 2]) frac[:, :, 0] -= -0.5 # xmin frac[:, :, 1] -= -0.5 # ymin frac[:, :, 2] -= -0.5 # zmin decimals = np.modf(frac)[0] # decimals[:,:,0] += xmin # decimals[:,:,1] += ymin # decimals[:,:,2] += zmin frac_wrapped = np.where(decimals < 0, 1 + decimals, decimals) # frac_wrapped[:,:,0] += xmin # frac_wrapped[:,:,1] += ymin # frac_wrapped[:,:,2] += zmin cart_wrapped = UnitCell.to_cartesian(cell, frac_wrapped) xyz_file.geometries = cart_wrapped xyz_file.write_to_file(file_name.rsplit(".", 1)[0] + "_wrapped.xyz")
def _setup_grid(self, cutoff, unit_cell, grid): """Choose a proper grid for the binning process""" if grid is None: # automatically choose a decent grid if unit_cell is None: grid = cutoff / 2.9 else: # The following would be faster, but it is not reliable # enough yet. #grid = unit_cell.get_optimal_subcell(cutoff/2.0) divisions = np.ceil(unit_cell.spacings / cutoff) divisions[divisions < 1] = 1 grid = unit_cell / divisions if isinstance(grid, float): grid_cell = UnitCell( np.array([[grid, 0, 0], [0, grid, 0], [0, 0, grid]])) elif isinstance(grid, UnitCell): grid_cell = grid else: raise TypeError( "Grid must be None, a float or a UnitCell instance.") if unit_cell is not None: # The columns of integer_matrix are the unit cell vectors in # fractional coordinates of the grid cell. integer_matrix = grid_cell.to_fractional( unit_cell.matrix.transpose()).transpose() if abs((integer_matrix - np.round(integer_matrix)) * self.unit_cell.active).max() > 1e-6: raise ValueError( "The unit cell vectors are not an integer linear combination of grid cell vectors." ) integer_matrix = integer_matrix.round() integer_cell = UnitCell(integer_matrix, unit_cell.active) else: integer_cell = None return grid_cell, integer_cell
def apply_to(self, x, columns=False): """Apply this transformation to the given object The argument can be several sorts of objects: * ``numpy.array`` with shape (3, ) * ``numpy.array`` with shape (N, 3) * ``numpy.array`` with shape (3, N), use ``columns=True`` * ``Translation`` * ``Rotation`` * ``Complete`` * ``UnitCell`` In case of arrays, the 3D vectors are transformed. In case of trans- formations, a transformation is returned that consists of this transformation applied AFTER the given translation. In case of a unit cell, a unit cell with rotated cell vectors is returned. (The translational part does not affect the unit cell.) This method is equivalent to self*x. """ if isinstance(x, numpy.ndarray) and len( x.shape) == 2 and x.shape[0] == 3 and columns: return numpy.dot(self.r, x) + self.t.reshape((3, 1)) if isinstance(x, numpy.ndarray) and ( x.shape == (3, ) or (len(x.shape) == 2 and x.shape[1] == 3)) and not columns: return numpy.dot(x, self.r.transpose()) + self.t elif isinstance(x, Complete): return Complete(numpy.dot(self.r, x.r), numpy.dot(self.r, x.t) + self.t) elif isinstance(x, Translation): return Complete(self.r, numpy.dot(self.r, x.t) + self.t) elif isinstance(x, Rotation): return Complete(numpy.dot(self.r, x.r), self.t) elif isinstance(x, UnitCell): return UnitCell(numpy.dot(self.r, x.matrix), x.active) else: raise ValueError("Can not apply this rotation to %s" % x)
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)
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)