Source code for pyscf.tools.chgcar

#!/usr/bin/env python
# Copyright 2014-2020 The PySCF Developers. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Paul J. Robinson <pjrobinson@ucla.edu>
#         Qiming Sun <osirpt.sun@gmail.com>
#

'''
Vasp CHGCAR file format

See also
https://cms.mpi.univie.ac.at/vasp/vasp/CHGCAR_file.html
'''

import collections
import time
import numpy
import pyscf
from pyscf import lib
from pyscf import gto
from pyscf.pbc import gto as pbcgto
from pyscf.tools import cubegen

RESOLUTION = cubegen.RESOLUTION
BOX_MARGIN = cubegen.BOX_MARGIN


[docs] def density(cell, outfile, dm, nx=60, ny=60, nz=60, resolution=RESOLUTION): '''Calculates electron density and write out in CHGCAR format. Args: cell : Mole or Cell object Mole or pbc Cell. If Mole object is given, the program will guess a cubic lattice for the molecule. outfile : str Name of Cube file to be written. dm : ndarray Density matrix of molecule. Kwargs: nx : int Number of grid point divisions in x direction. Note this is function of the molecule's size; a larger molecule will have a coarser representation than a smaller one for the same value. ny : int Number of grid point divisions in y direction. nz : int Number of grid point divisions in z direction. Returns: No return value. This function outputs a VASP chgcarlike file (with phase if desired)...it can be opened in VESTA or VMD or many other softwares Examples: >>> # generates the first MO from the list of mo_coefficents >>> from pyscf.pbc import gto, scf >>> from pyscf.tools import chgcar >>> cell = gto.M(atom='H 0 0 0; H 0 0 1', a=numpy.eye(3)*3) >>> mf = scf.RHF(cell).run() >>> chgcar.density(cell, 'h2.CHGCAR', mf.make_rdm1()) ''' cc = CHGCAR(cell, nx=nx, ny=ny, nz=nz, resolution=resolution) coords = cc.get_coords() ngrids = cc.get_ngrids() blksize = min(8000, ngrids) rho = numpy.empty(ngrids) for ip0, ip1 in lib.prange(0, ngrids, blksize): if isinstance(cell, pbcgto.cell.Cell): ao = cell.pbc_eval_gto('GTOval', coords[ip0:ip1]) else: ao = cell.eval_gto('GTOval', coords[ip0:ip1]) rho[ip0:ip1] = lib.einsum('pi,ij,pj->p', ao, dm, ao) rho = rho.reshape(nx,ny,nz) cc.write(rho, outfile)
[docs] def orbital(cell, outfile, coeff, nx=60, ny=60, nz=60, resolution=RESOLUTION): '''Calculate orbital value on real space grid and write out in CHGCAR format. Args: cell : Mole or Cell object Mole or pbc Cell. If Mole object is given, the program will guess a cubic lattice for the molecule. outfile : str Name of Cube file to be written. dm : ndarray Density matrix of molecule. Kwargs: nx : int Number of grid point divisions in x direction. Note this is function of the molecule's size; a larger molecule will have a coarser representation than a smaller one for the same value. ny : int Number of grid point divisions in y direction. nz : int Number of grid point divisions in z direction. Returns: No return value. This function outputs a VASP chgcarlike file (with phase if desired)...it can be opened in VESTA or VMD or many other softwares Examples: >>> # generates the first MO from the list of mo_coefficents >>> from pyscf.pbc import gto, scf >>> from pyscf.tools import chgcar >>> cell = gto.M(atom='H 0 0 0; H 0 0 1', a=numpy.eye(3)*3) >>> mf = scf.RHF(cell).run() >>> chgcar.orbital(cell, 'h2_mo1.CHGCAR', mf.mo_coeff[:,0]) ''' cc = CHGCAR(cell, nx=nx, ny=ny, nz=nz, resolution=resolution) coords = cc.get_coords() ngrids = cc.get_ngrids() blksize = min(8000, ngrids) orb_on_grid = numpy.empty(ngrids) for ip0, ip1 in lib.prange(0, ngrids, blksize): if isinstance(cell, pbcgto.cell.Cell): ao = cell.pbc_eval_gto('GTOval', coords[ip0:ip1]) else: ao = cell.eval_gto('GTOval', coords[ip0:ip1]) orb_on_grid[ip0:ip1] = numpy.dot(ao, coeff) orb_on_grid = orb_on_grid.reshape(nx,ny,nz) cc.write(orb_on_grid, outfile, comment='Orbital value in real space (1/Bohr^3)')
[docs] class CHGCAR(cubegen.Cube): ''' Read-write of the Vasp CHGCAR files ''' def __init__(self, cell, nx=60, ny=60, nz=60, resolution=RESOLUTION, margin=BOX_MARGIN): if not isinstance(cell, pbcgto.cell.Cell): coord = cell.atom_coords() box = numpy.max(coord,axis=0) - numpy.min(coord,axis=0) + margin*2 boxorig = numpy.min(coord,axis=0) - margin if resolution is not None: nx, ny, nz = numpy.ceil(box / resolution).astype(int) self.box = numpy.diag(box) lib.logger.warn(cell, 'Molecular system is found. FFT-grid is not ' 'available for Molecule. Lattice (in Bohr)\n' '%s\nand FFT grids %s are applied.', self.box, (nx,ny,nz)) self.mol = cell cell = cell.view(pbcgto.Cell) if (isinstance(cell.unit, str) and cell.unit.startswith(('B','b','au','AU'))): cell.a = self.box else: cell.a = self.box * lib.param.BOHR ptr = cell._atm[:,gto.PTR_COORD] cell._env[ptr+0] = coord[:,0] - boxorig[0] cell._env[ptr+1] = coord[:,1] - boxorig[1] cell._env[ptr+2] = coord[:,2] - boxorig[2] self.nx = nx self.ny = ny self.nz = nz self.cell = cell self.box = cell.lattice_vectors() self.boxorig = numpy.zeros(3) self.vol = cell.vol
[docs] def get_coords(self) : """ Result: set of coordinates to compute a field which is to be stored in the file. """ xs = numpy.arange(self.nx) * (1./self.nx) ys = numpy.arange(self.ny) * (1./self.ny) zs = numpy.arange(self.nz) * (1./self.nz) xyz = lib.cartesian_prod((xs, ys, zs)) coords = numpy.dot(xyz, self.box) return numpy.asarray(coords, order='C')
[docs] def write(self, field, fname, comment=None): """ Result: .vasp file with the field in the file fname. """ assert (field.ndim == 3) assert (field.shape == (self.nx, self.ny, self.nz)) if comment is None: comment = 'VASP file: Electron density in real space (e/Bohr^3) ' cell = self.cell # See CHGCAR format https://cms.mpi.univie.ac.at/vasp/vasp/CHGCAR_file.html # the value of (total density * volume) was dumped field = field * self.vol boxA = self.box * lib.param.BOHR atomList= [cell.atom_pure_symbol(i) for i in range(cell.natm)] Axyz = zip(atomList, cell.atom_coords().tolist()) Axyz = sorted(Axyz, key = lambda x: x[0]) swappedCoords = [(vec[1]+self.boxorig) * lib.param.BOHR for vec in Axyz] vaspAtomicInfo = collections.Counter([xyz[0] for xyz in Axyz]) vaspAtomicInfo = sorted(vaspAtomicInfo.items()) with open(fname, 'w') as f: f.write(comment) f.write('PySCF Version: %s Date: %s\n' % (pyscf.__version__, time.ctime())) f.write('1.0000000000\n') f.write('%14.8f %14.8f %14.8f \n' % (boxA[0,0],boxA[0,1],boxA[0,2])) f.write('%14.8f %14.8f %14.8f \n' % (boxA[1,0],boxA[1,1],boxA[1,2])) f.write('%14.8f %14.8f %14.8f \n' % (boxA[2,0],boxA[2,1],boxA[2,2])) f.write(''.join(['%5.3s'%atomN[0] for atomN in vaspAtomicInfo]) + '\n') f.write(''.join(['%5d'%atomN[1] for atomN in vaspAtomicInfo]) + '\n') f.write('Cartesian \n') for ia in range(cell.natm): f.write(' %14.8f %14.8f %14.8f\n' % tuple(swappedCoords[ia])) f.write('\n') f.write('%6.5s %6.5s %6.5s \n' % (self.nx,self.ny,self.nz)) fmt = ' %14.8e ' for iz in range(self.nz): for iy in range(self.ny): f.write('\n') for ix in range(self.nx): f.write(fmt % field[ix,iy,iz])
[docs] def read(self, chgcar_file): raise NotImplementedError
if __name__ == '__main__': from pyscf.pbc import scf from pyscf.tools import chgcar cell = gto.M(atom='H 0 0 0; H 0 0 1', a=numpy.eye(3)*3) mf = scf.RHF(cell).run() chgcar.density(cell, 'h2.CHGCAR', mf.make_rdm1()) #makes total density chgcar.orbital(cell, 'h2_mo1.CHGCAR', mf.mo_coeff[:,0]) # makes mo#1 (sigma) chgcar.orbital(cell, 'h2_mo2.CHGCAR', mf.mo_coeff[:,1]) # makes mo#2 (sigma*)