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 from_cell_str(cls, unit_cell_str, sub=slice(None)): sub = fix_slice(sub) if "," in unit_cell_str: parameters = list(parse_unit(word) for word in unit_cell_str.split(",") if len(word) > 0) if len(parameters) == 1: a = parameters[0] single = numpy.array([[a,0,0],[0,a,0],[0,0,a]], float) elif len(parameters) == 3: a,b,c = parameters single = numpy.array([[[a,0,0],[0,b,0],[0,0,c]]], float) elif len(parameters) == 6: a,b,c,alpha,beta,gamma = parameters uc = UnitCell( numpy.array([[1,0,0],[0,1,0],[0,0,1]], float), numpy.array([True, True, True]), ) uc.set_parameterst(numpy.array([a,b,c]), numpy.array([alpha,beta,gamma])) single = uc.cell elif len(parameters) == 9: single = numpy.array(parameters,float).reshape((3,3)).transpose() else: raise ValueError("If the --cell option contains comma's, one, three, six or nine value(s) are expected.") data = single.reshape((3,3,1)) else: data = [ [ load_track("%s.%s.%s" % (unit_cell_str, v, c), sub) for v in "abc" ] for c in "xyz" ] return cls(data)
def test_radius_indexes_2d(self): uc = UnitCell(numpy.identity(3, float), numpy.array([True, True, False])) indexes = uc.get_radius_indexes(0.5) expected_indexes = numpy.array([ [-1, -1, 0], [-1, 0, 0], [-1, 1, 0], [ 0, -1, 0], [ 0, 0, 0], [ 0, 1, 0], [ 1, -1, 0], [ 1, 0, 0], [ 1, 1, 0], ]) self.assertArraysEqual(indexes, expected_indexes)
def test_distance_matrix_periodic(self): for i in xrange(1000): N = 6 unit_cell = UnitCell( numpy.random.uniform(0,1,(3,3)), numpy.random.randint(0,2,3).astype(bool), ) fractional = numpy.random.uniform(0,1,(N,3)) coordinates = unit_cell.to_cartesian(fractional) from molmod.ext import molecules_distance_matrix dm = molecules_distance_matrix(coordinates, unit_cell.matrix, unit_cell.reciprocal) for i in xrange(N): for j in xrange(i,N): delta = coordinates[j]-coordinates[i] delta = unit_cell.shortest_vector(delta) distance = numpy.linalg.norm(delta) self.assertAlmostEqual(dm[i,j], distance)
def test_default_graph_periodic(self): molecule = Molecule.from_file("input/lau.xyz") uc = UnitCell.from_parameters3( numpy.array([14.587, 12.877, 7.613])*angstrom, numpy.array([90.000, 111.159, 90.000])*deg ) uc = uc.alignment_c*uc molecule = molecule.copy_with(unit_cell=uc) molecule.set_default_graph() self.assertEqual(molecule.graph.num_edges, 4*molecule.size/3)
def test_parameters(self): for counter in xrange(100): in_lengths = numpy.random.uniform(0.5, 1, (3,)) in_angles = numpy.random.uniform(0.3, numpy.pi/2, (3,)) try: uc = UnitCell.from_parameters3(in_lengths, in_angles) except ValueError, e: continue out_lengths, out_angles = uc.parameters self.assertArraysAlmostEqual(in_lengths, out_lengths) self.assertArraysAlmostEqual(in_angles, out_angles)
def get_random_ff(self): N = 6 mask = numpy.zeros((N,N), bool) for i in xrange(N): for j in xrange(i): mask[i,j] = True from molmod.ext import molecules_distance_matrix while True: unit_cell = UnitCell( numpy.random.uniform(0,3,(3,3)), numpy.random.randint(0,2,3).astype(bool), ) fractional = numpy.random.uniform(0,1,(N,3)) coordinates = unit_cell.to_cartesian(fractional) if numpy.random.randint(0,2): unit_cell = None dm = molecules_distance_matrix(coordinates) else: dm = molecules_distance_matrix(coordinates, unit_cell.matrix, unit_cell.reciprocal) if dm[mask].min() > 1.0: break edges = set([]) while len(edges) < 2*N: v1 = numpy.random.randint(N) while True: v2 = numpy.random.randint(N) if v2 != v1: break edges.add(frozenset([v1,v2])) edges = tuple(edges) numbers = numpy.random.randint(6, 10, N) graph = MolecularGraph(edges, numbers) ff = ToyFF(graph, unit_cell) return ff, coordinates, dm, mask, unit_cell
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 test_distances_intra_random_periodic(self): for i in xrange(10): coordinates = numpy.random.uniform(0,1,(20,3)) while True: unit_cell = UnitCell( numpy.random.uniform(0,5,(3,3)), numpy.random.randint(0,2,3).astype(bool), ) if unit_cell.spacings.min() > 0.5: break coordinates = unit_cell.to_cartesian(coordinates)*3-unit_cell.matrix.sum(axis=1) cutoff = numpy.random.uniform(1, 6) pair_search = PairSearchIntra(coordinates, cutoff, unit_cell) self.verify_bins_intra_periodic(pair_search.bins) distances = [ (frozenset([i0, i1]), distance) for i0, i1, delta, distance in pair_search ] self.verify_distances_intra(coordinates, cutoff, distances, unit_cell)
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 = numpy.ceil(unit_cell.spacings/cutoff) divisions[divisions<1] = 1 grid = unit_cell/divisions if isinstance(grid, float): grid_cell = UnitCell(numpy.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 - numpy.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 test_distances_inter_random_periodic(self): for i in xrange(10): fractional0 = numpy.random.uniform(0,1,(20,3)) fractional1 = numpy.random.uniform(0,1,(20,3)) while True: unit_cell = UnitCell( numpy.random.uniform(0,5,(3,3)), numpy.random.randint(0,2,3).astype(bool), ) if unit_cell.spacings.min() > 0.5: break coordinates0 = unit_cell.to_cartesian(fractional0)*3-unit_cell.matrix.sum(axis=1) coordinates1 = unit_cell.to_cartesian(fractional1)*3-unit_cell.matrix.sum(axis=1) cutoff = numpy.random.uniform(1, 6) pair_search = PairSearchInter(coordinates0, coordinates1, cutoff, unit_cell) self.verify_bins_inter_periodic(pair_search.bins0, pair_search.bins1) distances = [ ((i0, i1), distance) for i0, i1, delta, distance in pair_search ] self.verify_distances_inter(coordinates0, coordinates1, cutoff, distances, unit_cell)
def iter_unit_cells(unit_cell_str, sub=None): sub = fix_slice(sub) if len(unit_cell_str) == 0: uc = UnitCell( numpy.array([[1,0,0],[0,1,0],[0,0,1]], float), numpy.array([False,False,False]), ) while True: yield uc if "," in unit_cell_str: parameters = list(parse_unit(word) for word in unit_cell_str.split(",") if len(word) > 0) if len(parameters) == 1: a= parameters[0] uc = UnitCell( numpy.array([[a,0,0],[0,a,0],[0,0,a]], float), numpy.array([True, True, True]), ) elif len(parameters) == 3: a,b,c = parameters uc = UnitCell( numpy.array([[a,0,0],[0,b,0],[0,0,c]], float), numpy.array([True, True, True]), ) elif len(parameters) == 6: a,b,c,alpha,beta,gamma = parameters uc = UnitCell.from_parameters3( numpy.array([a,b,c]), numpy.array([alpha,beta,gamma]) ) elif len(parameters) == 9: uc = UnitCell( numpy.array(parameters, float).reshape((3,3)), numpy.array([True, True, True]), ) else: raise ValueError("If the --cell option contains comma's, one, three, six or nine value(s) are expected.") while True: yield uc else: filenames = ["%s.%s" % (unit_cell_str, suffix) for suffix in ["a.x", "a.y", "a.z", "b.x", "b.y", "b.z", "c.x", "c.y", "c.z"]] dtype = numpy.dtype([("cell", float, (3,3))]) mtr = MultiTracksReader(filenames, dtype, sub=sub) for row in mtr: yield UnitCell( numpy.array(row["cell"], float), numpy.array([True, True, True]), )
def test_distances_intra_lau_periodic(self): coordinates = XYZFile("input/lau.xyz").geometries[0] cutoff = periodic.max_radius*2 unit_cell = UnitCell.from_parameters3( numpy.array([14.59, 12.88, 7.61])*angstrom, numpy.array([ 90.0, 111.0, 90.0])*deg, ) pair_search = PairSearchIntra(coordinates, cutoff, unit_cell) self.verify_bins_intra_periodic(pair_search.bins) distances = [ (frozenset([i0, i1]), distance) for i0, i1, delta, distance in pair_search ] self.verify_distances_intra(coordinates, cutoff, distances, unit_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, 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. The energy without entropy (but not the extrapolation to sigma=0) is used. | outcar_freq -- The OUTCAR file of the Hessian/frequency calculation. Optional arguments: | 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]. """ # Read atomic symbols, coordinates and cell vectors from CONTCAR symbols = [] coordinates = [] with open(contcar) as f: # Skip title. f.next().strip() # Read scale for rvecs. rvec_scale = float(f.next()) # Read rvecs. VASP uses one row per cell vector. rvecs = np.fromstring(f.next()+f.next()+f.next(), sep=' ').reshape(3, 3) rvecs *= rvec_scale*angstrom unit_cell = UnitCell(rvecs) # Read symbols unique_symbols = f.next().split() # Read atom counts per symbol symbol_counts = [int(w) for w in f.next().split()] assert len(symbol_counts) == len(unique_symbols) natom = sum(symbol_counts) # Construct array with atomic numbers. numbers = [] for iunique in xrange(len(unique_symbols)): number = periodic[unique_symbols[iunique]].number numbers.extend([number]*symbol_counts[iunique]) numbers = np.array(numbers) # Check next line while f.next() != 'Direct\n': continue # Load fractional coordinates fractional = np.zeros((natom, 3), float) for iatom in xrange(natom): words = f.next().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) # 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 = f.next() 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 f.next() gradient = np.zeros((natom, 3), float) gunit = electronvolt/angstrom for iatom in xrange(natom): words = f.next().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: # 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 f.next() f.next() f.next() energy = float(f.next().split()[3])*electronvolt # Go to the second derivatives for line in f: if line.startswith(' SECOND DERIVATIVES (NOT SYMMETRIZED)'): break # Skip one line. f.next() # Load free atoms (not fixed in space). keys = f.next().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 xrange(nfree_dof): line = f.next() irow = indices_free[ifree0] # skip first col words = line.split()[1:] assert len(words) == nfree_dof for ifree1 in xrange(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)
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)
def test_add_periodicities(self): for counter in xrange(100): uc0 = UnitCell(numpy.identity(3, float), numpy.zeros(3,bool)) uc1 = uc0.add_cell_vector(numpy.random.uniform(-2,2,3)) uc2 = uc1.add_cell_vector(numpy.random.uniform(-2,2,3)) uc3 = uc2.add_cell_vector(numpy.random.uniform(-2,2,3))
def test_shortest_vector(self): raise SkipTest # simple cases uc = UnitCell(numpy.identity(3,float)*3) self.assertArraysAlmostEqual(uc.shortest_vector([3, 0, 1]), numpy.array([0, 0, 1])) self.assertArraysAlmostEqual(uc.shortest_vector([-3, 0, 1]), numpy.array([0, 0, 1])) self.assertArraysAlmostEqual(uc.shortest_vector([-2, 0, 1]), numpy.array([1, 0, 1])) self.assertArraysAlmostEqual(uc.shortest_vector([-1.6, 1, 1]), numpy.array([1.4, 1, 1])) self.assertArraysAlmostEqual(uc.shortest_vector([-1.4, 1, 1]), numpy.array([-1.4, 1, 1])) # simple cases uc = UnitCell(numpy.identity(3,float)*3, numpy.array([True, False, False])) self.assertArraysAlmostEqual(uc.shortest_vector([3, 0, 1]), numpy.array([0, 0, 1])) self.assertArraysAlmostEqual(uc.shortest_vector([3, 0, 3]), numpy.array([0, 0, 3])) # random tests for uc_counter in xrange(1000): uc = self.get_random_uc(full=False) for r_counter in xrange(10): r0 = numpy.random.normal(0, 10, 3) r1 = uc.shortest_vector(r0) change = r1 - r0 self.assert_(numpy.dot(change, r0) <= 0) #self.assert_(numpy.linalg.norm(r0) >= numpy.linalg.norm(r1)) index = uc.to_fractional(r0-r1) self.assertArraysAlmostEqual(index, numpy.round(index), doabs=True) index = uc.to_fractional(r1) self.assert_(index.max()<0.5) self.assert_(index.max()>=-0.5) r0 = numpy.random.normal(0, 10, (10,3)) r1 = uc.shortest_vector(r0) for i in xrange(10): r1_row_bis = uc.shortest_vector(r0[i]) self.assertArraysAlmostEqual(r1_row_bis, r1[i], doabs=True)
def test_shortest_vector_trivial(self): uc = UnitCell(numpy.identity(3, float)) half = numpy.array([0.5,0.5,0.5]) self.assertArraysEqual(uc.shortest_vector(half), -half) self.assertArraysEqual(uc.shortest_vector(-half), -half)
def test_shortest_vector_aperiodic(self): unit_cell = UnitCell(numpy.identity(3, float), numpy.zeros(3, bool)) shortest = unit_cell.shortest_vector(numpy.ones(3, float)) expected = numpy.ones(3, float) self.assertArraysAlmostEqual(shortest, expected)
def test_radius_indexes_2d_graphical(self): #uc = UnitCell(numpy.array([ # [2.0, 1.0, 0.0], # [0.0, 0.2, 0.0], # [0.0, 0.0, 10.0], #])) #radius = 0.8 uc = UnitCell(numpy.array([ [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 10.0], ])) radius = 5.3 #uc = UnitCell(numpy.array([ # [1.0, 1.0, 0.0], # [0.0, 1.0, 0.0], # [0.0, 0.0, 1.0], #])) #radius = 0.9 fracs = numpy.arange(-0.5, 0.55, 0.1) import pylab from matplotlib.patches import Circle, Polygon from matplotlib.lines import Line2D pylab.clf() for i0 in fracs: for i1 in fracs: center = uc.to_cartesian([i0,i1,0.0]) pylab.gca().add_artist(Circle((center[0], center[1]), radius, fill=True, fc='#7777AA', ec='none')) pylab.gca().add_artist(Circle((0, 0), radius, fill=True, fc='#0000AA', ec='none')) ranges = uc.get_radius_ranges(radius) indexes = uc.get_radius_indexes(radius) for i in xrange(-ranges[0]-1, ranges[0]+1): start = uc.to_cartesian([i+0.5, -ranges[1]-0.5, 0]) end = uc.to_cartesian([i+0.5, ranges[1]+0.5, 0]) pylab.gca().add_artist(Line2D([start[0], end[0]], [start[1], end[1]], color="k", linewidth=1)) for i in xrange(-ranges[1]-1, ranges[1]+1): start = uc.to_cartesian([-ranges[0]-0.5, i+0.5, 0]) end = uc.to_cartesian([ranges[0]+0.5, i+0.5, 0]) pylab.gca().add_artist(Line2D([start[0], end[0]], [start[1], end[1]], color="k", linewidth=1)) for i in xrange(-ranges[0], ranges[0]+1): start = uc.to_cartesian([i, -ranges[1]-0.5, 0]) end = uc.to_cartesian([i, ranges[1]+0.5, 0]) pylab.gca().add_artist(Line2D([start[0], end[0]], [start[1], end[1]], color="k", linewidth=0.5, linestyle="--")) for i in xrange(-ranges[1], ranges[1]+1): start = uc.to_cartesian([-ranges[0]-0.5, i, 0]) end = uc.to_cartesian([ranges[0]+0.5, i, 0]) pylab.gca().add_artist(Line2D([start[0], end[0]], [start[1], end[1]], color="k", linewidth=0.5, linestyle="--")) for i0,i1,i2 in indexes: if i2 != 0: continue corners = uc.to_cartesian(numpy.array([ [i0-0.5, i1-0.5, 0.0], [i0-0.5, i1+0.5, 0.0], [i0+0.5, i1+0.5, 0.0], [i0+0.5, i1-0.5, 0.0], ])) pylab.gca().add_artist(Polygon(corners[:,:2], fill=True, ec='none', fc='r', alpha=0.5)) corners = uc.to_cartesian(numpy.array([ [-ranges[0]-0.5, -ranges[1]-0.5, 0.0], [-ranges[0]-0.5, +ranges[1]+0.5, 0.0], [+ranges[0]+0.5, +ranges[1]+0.5, 0.0], [+ranges[0]+0.5, -ranges[1]-0.5, 0.0], ])) pylab.xlim(1.1*corners[:,:2].min(), 1.1*corners[:,:2].max()) pylab.ylim(1.1*corners[:,:2].min(), 1.1*corners[:,:2].max()) #pylab.xlim(-1.5*radius, 1.5*radius) #pylab.ylim(-1.5*radius, 1.5*radius) pylab.savefig("output/radius_indexes_2d.png")
def test_radius_ranges_2d(self): uc = UnitCell(numpy.identity(3, float), numpy.array([True, True, False])) self.assertArraysEqual(uc.get_radius_ranges(3), numpy.array([3,3,0]))