def create_symmetry(self, magmom_a, cell_cv): symm = self.parameters.symmetry if symm == 'off': symm = {'point_group': False, 'time_reversal': False} m_a = magmom_a.round(decimals=3) # round off id_a = list(zip(self.setups.id_a, m_a)) self.symmetry = Symmetry(id_a, cell_cv, self.atoms.pbc, **symm) self.symmetry.analyze(self.atoms.get_scaled_positions()) self.setups.set_symmetry(self.symmetry)
def create_symmetry(self, magmom_av, cell_cv): symm = self.parameters.symmetry if symm == 'off': symm = {'point_group': False, 'time_reversal': False} m_av = magmom_av.round(decimals=3) # round off id_a = [id + tuple(m_v) for id, m_v in zip(self.setups.id_a, m_av)] self.symmetry = Symmetry(id_a, cell_cv, self.atoms.pbc, **symm) self.symmetry.analyze(self.spos_ac) self.setups.set_symmetry(self.symmetry)
def set_symmetry(self, atoms, setups, usesymm, N_c=None): """Create symmetry object and construct irreducible Brillouin zone. Parameters ---------- atoms: Atoms object Defines atom positions and types and also unit cell and boundary conditions. setups: instance of class Setups PAW setups for the atoms. usesymm: bool Symmetry flag. N_c: three int's or None If not None: Check also symmetry of grid. """ if (~atoms.pbc & self.bzk_kc.any(0)).any(): raise ValueError('K-points can only be used with PBCs!') # Construct a Symmetry instance containing the identity operation # only # Round off magmom_a = atoms.get_initial_magnetic_moments().round(decimals=3) id_a = zip(magmom_a, setups.id_a) self.symmetry = Symmetry(id_a, atoms.cell / Bohr, atoms.pbc) if self.gamma or usesymm is None: # Point group and time-reversal symmetry neglected nkpts = len(self.bzk_kc) self.weight_k = np.ones(nkpts) / nkpts self.ibzk_kc = self.bzk_kc.copy() self.sym_k = np.zeros(nkpts) self.time_reversal_k = np.zeros(nkpts, bool) self.kibz_k = np.arange(nkpts) else: if usesymm: # Find symmetry operations of atoms self.symmetry.analyze(atoms.get_scaled_positions()) if N_c is not None: self.symmetry.prune_symmetries_grid(N_c) (self.ibzk_kc, self.weight_k, self.sym_k, self.time_reversal_k, self.kibz_k) = self.symmetry.reduce(self.bzk_kc) setups.set_symmetry(self.symmetry) # Number of irreducible k-points and k-point/spin combinations. self.nibzkpts = len(self.ibzk_kc) self.nks = self.nibzkpts * self.nspins
def get_lattice_symmetry(cell_cv, tolerance=1e-7): """Return symmetry object of lattice group. Parameters ---------- cell_cv : ndarray Unit cell. Returns ------- gpaw.symmetry object """ latsym = Symmetry([0], cell_cv, tolerance=tolerance) latsym.find_lattice_symmetry() return latsym
) atoms.set_calculator(calc) atoms.get_potential_energy() # "Bandstructure" calculation (only Gamma point here) kpts = np.array(((0, 0, 0), )) calc.set(fixdensity=True, kpts=kpts) atoms.get_potential_energy() calc.write('Si_gamma.gpw', mode='all') # Analyse symmetries of wave functions atoms, calc = restart('Si_gamma.gpw', txt=None) # Find symmetries (in Gamma-point calculations calculator does not # use symmetry) sym = Symmetry(calc.wfs.setups.id_a, atoms.cell, atoms.pbc) sym.analyze(atoms.get_scaled_positions()) def find_classes(op_all_scc): # Find classes of group represented by matrices op_all_scc # and return representative operations op_scc = [op_all_scc[0]] for op1 in op_all_scc[1:]: new_class = True for op2 in op_all_scc: op_tmp = (np.dot(np.dot(op2, op1), np.linalg.inv(op2).astype(int))) # Check whether operation op1 belongs to existing class for op in op_scc: if np.all((op_tmp - op) == 0): new_class = False
def get_realspace_hs(h_skmm, s_kmm, bzk_kc, weight_k, R_c=(0, 0, 0), direction='x', symmetry={'enabled': False}): from gpaw.symmetry import Symmetry from ase.dft.kpoints import get_monkhorst_pack_size_and_offset, \ monkhorst_pack if symmetry['point_group'] is True: raise NotImplementedError, 'Point group symmetry not implemented' nspins, nk, nbf = h_skmm.shape[:3] dir = 'xyz'.index(direction) transverse_dirs = np.delete([0, 1, 2], [dir]) dtype = float if len(bzk_kc) > 1 or np.any(bzk_kc[0] != [0, 0, 0]): dtype = complex kpts_grid = get_monkhorst_pack_size_and_offset(bzk_kc)[0] # kpts in the transport direction nkpts_p = kpts_grid[dir] bzk_p_kc = monkhorst_pack((nkpts_p,1,1))[:, 0] weight_p_k = 1. / nkpts_p # kpts in the transverse directions bzk_t_kc = monkhorst_pack(tuple(kpts_grid[transverse_dirs]) + (1, )) if not 'time_reversal' in symmetry: symmetry['time_reversal'] = True if symmetry['time_reversal'] is True: #XXX a somewhat ugly hack: # By default GPAW reduces inversion sym in the z direction. The steps # below assure reduction in the transverse dirs. # For now this part seems to do the job, but it may be written # in a smarter way in the future. symmetry = Symmetry([1], np.eye(3)) symmetry.prune_symmetries_atoms(np.zeros((1, 3))) ibzk_kc, ibzweight_k = symmetry.reduce(bzk_kc)[:2] ibzk_t_kc, weights_t_k = symmetry.reduce(bzk_t_kc)[:2] ibzk_t_kc = ibzk_t_kc[:, :2] nkpts_t = len(ibzk_t_kc) else: ibzk_kc = bzk_kc.copy() ibzk_t_kc = bzk_t_kc nkpts_t = len(bzk_t_kc) weights_t_k = [1. / nkpts_t for k in range(nkpts_t)] h_skii = np.zeros((nspins, nkpts_t, nbf, nbf), dtype) if s_kmm is not None: s_kii = np.zeros((nkpts_t, nbf, nbf), dtype) tol = 7 for j, k_t in enumerate(ibzk_t_kc): for k_p in bzk_p_kc: k = np.zeros((3,)) k[dir] = k_p k[transverse_dirs] = k_t kpoint_list = [list(np.round(k_kc, tol)) for k_kc in ibzk_kc] if list(np.round(k, tol)) not in kpoint_list: k = -k # inversion index = kpoint_list.index(list(np.round(k,tol))) h = h_skmm[:, index].conjugate() if s_kmm is not None: s = s_kmm[index].conjugate() k=-k else: # kpoint in the ibz index = kpoint_list.index(list(np.round(k, tol))) h = h_skmm[:, index] if s_kmm is not None: s = s_kmm[index] c_k = np.exp(2.j * np.pi * np.dot(k, R_c)) * weight_p_k h_skii[:, j] += c_k * h if s_kmm is not None: s_kii[j] += c_k * s if s_kmm is None: return ibzk_t_kc, weights_t_k, h_skii else: return ibzk_t_kc, weights_t_k, h_skii, s_kii
def __init__(self, calc, gamma=True, symmetry=False, e_ph=False, communicator=serial_comm): """Inititialize class with a list of atoms. The atoms object must contain a converged ground-state calculation. The set of q-vectors in which the dynamical matrix will be calculated is determined from the ``symmetry`` kwarg. For now, only time-reversal symmetry is used to generate the irrecducible BZ. Add a little note on parallelization strategy here. Parameters ---------- calc: str or Calculator Calculator containing a ground-state calculation. gamma: bool Gamma-point calculation with respect to the q-vector of the dynamical matrix. When ``False``, the Monkhorst-Pack grid from the ground-state calculation is used. symmetry: bool Use symmetries to reduce the q-vectors of the dynamcial matrix (None, False or True). The different options are equivalent to the old style options in a ground-state calculation (see usesymm). e_ph: bool Save the derivative of the effective potential. communicator: Communicator Communicator for parallelization over k-points and real-space domain. """ # XXX assert symmetry in [None, False], "Spatial symmetries not allowed yet" if isinstance(calc, str): self.calc = GPAW(calc, communicator=serial_comm, txt=None) else: self.calc = calc cell_cv = self.calc.atoms.get_cell() setups = self.calc.wfs.setups # XXX - no clue how to get magmom - ignore it for the moment # m_av = magmom_av.round(decimals=3) # round off # id_a = zip(setups.id_a, *m_av.T) id_a = setups.id_a if symmetry is None: self.symmetry = Symmetry(id_a, cell_cv, point_group=False, time_reversal=False) else: self.symmetry = Symmetry(id_a, cell_cv, point_group=False, time_reversal=True) # Make sure localized functions are initialized self.calc.set_positions() # Note that this under some circumstances (e.g. when called twice) # allocates a new array for the P_ani coefficients !! # Store useful objects self.atoms = self.calc.get_atoms() # Get rid of ``calc`` attribute self.atoms.calc = None # Boundary conditions pbc_c = self.calc.atoms.get_pbc() if np.all(pbc_c == False): self.gamma = True self.dtype = float kpts = None # Multigrid Poisson solver poisson_solver = PoissonSolver() else: if gamma: self.gamma = True self.dtype = float kpts = None else: self.gamma = False self.dtype = complex # Get k-points from ground-state calculation kpts = self.calc.input_parameters.kpts # FFT Poisson solver poisson_solver = FFTPoissonSolver(dtype=self.dtype) # K-point descriptor for the q-vectors of the dynamical matrix # Note, no explicit parallelization here. self.kd = KPointDescriptor(kpts, 1) self.kd.set_symmetry(self.atoms, self.symmetry) self.kd.set_communicator(serial_comm) # Number of occupied bands nvalence = self.calc.wfs.nvalence nbands = nvalence // 2 + nvalence % 2 assert nbands <= self.calc.wfs.bd.nbands # Extract other useful objects # Ground-state k-point descriptor - used for the k-points in the # ResponseCalculator # XXX replace communicators when ready to parallelize kd_gs = self.calc.wfs.kd gd = self.calc.density.gd kpt_u = self.calc.wfs.kpt_u setups = self.calc.wfs.setups dtype_gs = self.calc.wfs.dtype # WaveFunctions wfs = WaveFunctions(nbands, kpt_u, setups, kd_gs, gd, dtype=dtype_gs) # Linear response calculator self.response_calc = ResponseCalculator(self.calc, wfs, dtype=self.dtype) # Phonon perturbation self.perturbation = PhononPerturbation(self.calc, self.kd, poisson_solver, dtype=self.dtype) # Dynamical matrix self.dyn = DynamicalMatrix(self.atoms, self.kd, dtype=self.dtype) # Electron-phonon couplings if e_ph: self.e_ph = ElectronPhononCoupling(self.atoms, gd, self.kd, dtype=self.dtype) else: self.e_ph = None # Initialization flag self.initialized = False # Parallel communicator for parallelization over kpts and domain self.comm = communicator
from math import sqrt import numpy as np from gpaw.symmetry import Symmetry from ase.dft.kpoints import monkhorst_pack # Primitive diamond lattice, with Si lattice parameter a = 5.475 cell_cv = .5 * a * np.array([(1, 1, 0), (1, 0, 1), (0, 1, 1)]) spos_ac = np.array([(.00, .00, .00), (.25, .25, .25)]) id_a = [1, 1] # Two identical atoms pbc_c = np.ones(3, bool) bzk_kc = monkhorst_pack((4, 4, 4)) # Do check symm = Symmetry(id_a, cell_cv, pbc_c, symmorphic=False) symm.analyze(spos_ac) ibzk_kc, w_k = symm.reduce(bzk_kc)[:2] assert len(symm.op_scc) == 48 assert len(w_k) == 10 a = 3 / 32. b = 1 / 32. c = 6 / 32. assert np.all(w_k == [a, b, a, c, c, a, a, a, a, b]) assert not symm.op_scc.sum(0).any() # Rotate unit cell and check again: cell_cv = a / sqrt(2) * np.array([(1, 0, 0), (0.5, sqrt(3) / 2, 0), (0.5, sqrt(3) / 6, sqrt(2.0 / 3))]) symm = Symmetry(id_a, cell_cv, pbc_c, symmorphic=False) symm.analyze(spos_ac)
def initialize(self, atoms=None): """Inexpensive initialization.""" if atoms is None: atoms = self.atoms else: # Save the state of the atoms: self.atoms = atoms.copy() par = self.input_parameters world = par.communicator if world is None: world = mpi.world elif hasattr(world, 'new_communicator'): # Check for whether object has correct type already # # Using isinstance() is complicated because of all the # combinations, serial/parallel/debug... pass else: # world should be a list of ranks: world = mpi.world.new_communicator(np.asarray(world)) self.wfs.world = world if 'txt' in self._changed_keywords: self.set_txt(par.txt) self.verbose = par.verbose natoms = len(atoms) cell_cv = atoms.get_cell() / Bohr pbc_c = atoms.get_pbc() Z_a = atoms.get_atomic_numbers() magmom_av = atoms.get_initial_magnetic_moments() self.check_atoms() # Generate new xc functional only when it is reset by set # XXX sounds like this should use the _changed_keywords dictionary. if self.hamiltonian is None or self.hamiltonian.xc is None: if isinstance(par.xc, str): xc = XC(par.xc) else: xc = par.xc else: xc = self.hamiltonian.xc mode = par.mode if mode == 'fd': mode = FD() elif mode == 'pw': mode = pw.PW() elif mode == 'lcao': mode = LCAO() else: assert hasattr(mode, 'name'), str(mode) if xc.orbital_dependent and mode.name == 'lcao': raise NotImplementedError('LCAO mode does not support ' 'orbital-dependent XC functionals.') if par.realspace is None: realspace = (mode.name != 'pw') else: realspace = par.realspace if mode.name == 'pw': assert not realspace if par.filter is None and mode.name != 'pw': gamma = 1.6 if par.gpts is not None: h = ((np.linalg.inv(cell_cv)**2).sum(0)**-0.5 / par.gpts).max() else: h = (par.h or 0.2) / Bohr def filter(rgd, rcut, f_r, l=0): gcut = np.pi / h - 2 / rcut / gamma f_r[:] = rgd.filter(f_r, rcut * gamma, gcut, l) else: filter = par.filter setups = Setups(Z_a, par.setups, par.basis, par.lmax, xc, filter, world) if magmom_av.ndim == 1: collinear = True magmom_av, magmom_a = np.zeros((natoms, 3)), magmom_av magmom_av[:, 2] = magmom_a else: collinear = False magnetic = magmom_av.any() spinpol = par.spinpol if par.hund: if natoms != 1: raise ValueError('hund=True arg only valid for single atoms!') spinpol = True magmom_av[0] = (0, 0, setups[0].get_hunds_rule_moment(par.charge)) if spinpol is None: spinpol = magnetic elif magnetic and not spinpol: raise ValueError('Non-zero initial magnetic moment for a ' + 'spin-paired calculation!') if collinear: nspins = 1 + int(spinpol) ncomp = 1 else: nspins = 1 ncomp = 2 if par.usesymm != 'default': warnings.warn('Use "symmetry" keyword instead of ' + '"usesymm" keyword') par.symmetry = usesymm2symmetry(par.usesymm) symm = par.symmetry if symm == 'off': symm = {'point_group': False, 'time_reversal': False} bzkpts_kc = kpts2ndarray(par.kpts, self.atoms) kd = KPointDescriptor(bzkpts_kc, nspins, collinear) m_av = magmom_av.round(decimals=3) # round off id_a = zip(setups.id_a, *m_av.T) symmetry = Symmetry(id_a, cell_cv, atoms.pbc, **symm) kd.set_symmetry(atoms, symmetry, comm=world) setups.set_symmetry(symmetry) if par.gpts is not None: N_c = np.array(par.gpts) else: h = par.h if h is not None: h /= Bohr N_c = get_number_of_grid_points(cell_cv, h, mode, realspace, kd.symmetry) symmetry.check_grid(N_c) width = par.width if width is None: if pbc_c.any(): width = 0.1 # eV else: width = 0.0 else: assert par.occupations is None if hasattr(self, 'time') or par.dtype == complex: dtype = complex else: if kd.gamma: dtype = float else: dtype = complex nao = setups.nao nvalence = setups.nvalence - par.charge M_v = magmom_av.sum(0) M = np.dot(M_v, M_v)**0.5 nbands = par.nbands orbital_free = any(setup.orbital_free for setup in setups) if orbital_free: nbands = 1 if isinstance(nbands, basestring): if nbands[-1] == '%': basebands = int(nvalence + M + 0.5) // 2 nbands = int((float(nbands[:-1]) / 100) * basebands) else: raise ValueError('Integer Expected: Only use a string ' 'if giving a percentage of occupied bands') if nbands is None: nbands = 0 for setup in setups: nbands_from_atom = setup.get_default_nbands() # Any obscure setup errors? if nbands_from_atom < -(-setup.Nv // 2): raise ValueError('Bad setup: This setup requests %d' ' bands but has %d electrons.' % (nbands_from_atom, setup.Nv)) nbands += nbands_from_atom nbands = min(nao, nbands) elif nbands > nao and mode.name == 'lcao': raise ValueError('Too many bands for LCAO calculation: ' '%d bands and only %d atomic orbitals!' % (nbands, nao)) if nvalence < 0: raise ValueError( 'Charge %f is not possible - not enough valence electrons' % par.charge) if nbands <= 0: nbands = int(nvalence + M + 0.5) // 2 + (-nbands) if nvalence > 2 * nbands and not orbital_free: raise ValueError('Too few bands! Electrons: %f, bands: %d' % (nvalence, nbands)) nbands *= ncomp if par.width is not None: self.text('**NOTE**: please start using ' 'occupations=FermiDirac(width).') if par.fixmom: self.text('**NOTE**: please start using ' 'occupations=FermiDirac(width, fixmagmom=True).') if self.occupations is None: if par.occupations is None: # Create object for occupation numbers: if orbital_free: width = 0.0 # even for PBC self.occupations = occupations.TFOccupations( width, par.fixmom) else: self.occupations = occupations.FermiDirac( width, par.fixmom) else: self.occupations = par.occupations # If occupation numbers are changed, and we have wave functions, # recalculate the occupation numbers if self.wfs is not None and not isinstance(self.wfs, EmptyWaveFunctions): self.occupations.calculate(self.wfs) self.occupations.magmom = M_v[2] cc = par.convergence if mode.name == 'lcao': niter_fixdensity = 0 else: niter_fixdensity = None if self.scf is None: force_crit = cc['forces'] if force_crit is not None: force_crit /= Hartree / Bohr self.scf = SCFLoop(cc['eigenstates'] / Hartree**2 * nvalence, cc['energy'] / Hartree * max(nvalence, 1), cc['density'] * nvalence, par.maxiter, par.fixdensity, niter_fixdensity, force_crit) parsize_kpt = par.parallel['kpt'] parsize_domain = par.parallel['domain'] parsize_bands = par.parallel['band'] if not realspace: pbc_c = np.ones(3, bool) if not self.wfs: if parsize_domain == 'domain only': # XXX this was silly! parsize_domain = world.size parallelization = mpi.Parallelization(world, nspins * kd.nibzkpts) ndomains = None if parsize_domain is not None: ndomains = np.prod(parsize_domain) if mode.name == 'pw': if ndomains > 1: raise ValueError('Planewave mode does not support ' 'domain decomposition.') ndomains = 1 parallelization.set(kpt=parsize_kpt, domain=ndomains, band=parsize_bands) comms = parallelization.build_communicators() domain_comm = comms['d'] kpt_comm = comms['k'] band_comm = comms['b'] kptband_comm = comms['D'] domainband_comm = comms['K'] self.comms = comms kd.set_communicator(kpt_comm) parstride_bands = par.parallel['stridebands'] # Unfortunately we need to remember that we adjusted the # number of bands so we can print a warning if it differs # from the number specified by the user. (The number can # be inferred from the input parameters, but it's tricky # because we allow negative numbers) self.nbands_parallelization_adjustment = -nbands % band_comm.size nbands += self.nbands_parallelization_adjustment # I would like to give the following error message, but apparently # there are cases, e.g. gpaw/test/gw_ppa.py, which involve # nbands > nao and are supposed to work that way. #if nbands > nao: # raise ValueError('Number of bands %d adjusted for band ' # 'parallelization %d exceeds number of atomic ' # 'orbitals %d. This problem can be fixed ' # 'by reducing the number of bands a bit.' # % (nbands, band_comm.size, nao)) bd = BandDescriptor(nbands, band_comm, parstride_bands) if (self.density is not None and self.density.gd.comm.size != domain_comm.size): # Domain decomposition has changed, so we need to # reinitialize density and hamiltonian: if par.fixdensity: raise RuntimeError( 'Density reinitialization conflict ' + 'with "fixdensity" - specify domain decomposition.') self.density = None self.hamiltonian = None # Construct grid descriptor for coarse grids for wave functions: gd = self.grid_descriptor_class(N_c, cell_cv, pbc_c, domain_comm, parsize_domain) # do k-point analysis here? XXX args = (gd, nvalence, setups, bd, dtype, world, kd, kptband_comm, self.timer) if par.parallel['sl_auto']: # Choose scalapack parallelization automatically for key, val in par.parallel.items(): if (key.startswith('sl_') and key != 'sl_auto' and val is not None): raise ValueError("Cannot use 'sl_auto' together " "with '%s'" % key) max_scalapack_cpus = bd.comm.size * gd.comm.size nprow = max_scalapack_cpus npcol = 1 # Get a sort of reasonable number of columns/rows while npcol < nprow and nprow % 2 == 0: npcol *= 2 nprow //= 2 assert npcol * nprow == max_scalapack_cpus # ScaLAPACK creates trouble if there aren't at least a few # whole blocks; choose block size so there will always be # several blocks. This will crash for small test systems, # but so will ScaLAPACK in any case blocksize = min(-(-nbands // 4), 64) sl_default = (nprow, npcol, blocksize) else: sl_default = par.parallel['sl_default'] if mode.name == 'lcao': # Layouts used for general diagonalizer sl_lcao = par.parallel['sl_lcao'] if sl_lcao is None: sl_lcao = sl_default lcaoksl = get_KohnSham_layouts(sl_lcao, 'lcao', gd, bd, domainband_comm, dtype, nao=nao, timer=self.timer) self.wfs = mode(collinear, lcaoksl, *args) elif mode.name == 'fd' or mode.name == 'pw': # buffer_size keyword only relevant for fdpw buffer_size = par.parallel['buffer_size'] # Layouts used for diagonalizer sl_diagonalize = par.parallel['sl_diagonalize'] if sl_diagonalize is None: sl_diagonalize = sl_default diagksl = get_KohnSham_layouts( sl_diagonalize, 'fd', # XXX # choice of key 'fd' not so nice gd, bd, domainband_comm, dtype, buffer_size=buffer_size, timer=self.timer) # Layouts used for orthonormalizer sl_inverse_cholesky = par.parallel['sl_inverse_cholesky'] if sl_inverse_cholesky is None: sl_inverse_cholesky = sl_default if sl_inverse_cholesky != sl_diagonalize: message = 'sl_inverse_cholesky != sl_diagonalize ' \ 'is not implemented.' raise NotImplementedError(message) orthoksl = get_KohnSham_layouts(sl_inverse_cholesky, 'fd', gd, bd, domainband_comm, dtype, buffer_size=buffer_size, timer=self.timer) # Use (at most) all available LCAO for initialization lcaonbands = min(nbands, nao) try: lcaobd = BandDescriptor(lcaonbands, band_comm, parstride_bands) except RuntimeError: initksl = None else: # Layouts used for general diagonalizer # (LCAO initialization) sl_lcao = par.parallel['sl_lcao'] if sl_lcao is None: sl_lcao = sl_default initksl = get_KohnSham_layouts(sl_lcao, 'lcao', gd, lcaobd, domainband_comm, dtype, nao=nao, timer=self.timer) if hasattr(self, 'time'): assert mode.name == 'fd' from gpaw.tddft import TimeDependentWaveFunctions self.wfs = TimeDependentWaveFunctions( par.stencils[0], diagksl, orthoksl, initksl, gd, nvalence, setups, bd, world, kd, kptband_comm, self.timer) elif mode.name == 'fd': self.wfs = mode(par.stencils[0], diagksl, orthoksl, initksl, *args) else: assert mode.name == 'pw' self.wfs = mode(diagksl, orthoksl, initksl, *args) else: self.wfs = mode(self, *args) else: self.wfs.set_setups(setups) if not self.wfs.eigensolver: # Number of bands to converge: nbands_converge = cc['bands'] if nbands_converge == 'all': nbands_converge = nbands elif nbands_converge != 'occupied': assert isinstance(nbands_converge, int) if nbands_converge < 0: nbands_converge += nbands eigensolver = get_eigensolver(par.eigensolver, mode, par.convergence) eigensolver.nbands_converge = nbands_converge # XXX Eigensolver class doesn't define an nbands_converge property if isinstance(xc, SIC): eigensolver.blocksize = 1 self.wfs.set_eigensolver(eigensolver) if self.density is None: gd = self.wfs.gd if par.stencils[1] != 9: # Construct grid descriptor for fine grids for densities # and potentials: finegd = gd.refine() else: # Special case (use only coarse grid): finegd = gd if realspace: self.density = RealSpaceDensity( gd, finegd, nspins, par.charge + setups.core_charge, collinear, par.stencils[1]) else: self.density = pw.ReciprocalSpaceDensity( gd, finegd, nspins, par.charge + setups.core_charge, collinear) self.density.initialize(setups, self.timer, magmom_av, par.hund) self.density.set_mixer(par.mixer) if self.hamiltonian is None: gd, finegd = self.density.gd, self.density.finegd if realspace: self.hamiltonian = RealSpaceHamiltonian( gd, finegd, nspins, setups, self.timer, xc, world, self.wfs.kptband_comm, par.external, collinear, par.poissonsolver, par.stencils[1]) else: self.hamiltonian = pw.ReciprocalSpaceHamiltonian( gd, finegd, self.density.pd2, self.density.pd3, nspins, setups, self.timer, xc, world, self.wfs.kptband_comm, par.external, collinear) xc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) self.text() self.print_memory_estimate(self.txt, maxdepth=memory_estimate_depth) self.txt.flush() self.timer.print_info(self) if dry_run: self.dry_run() if realspace and \ self.hamiltonian.poisson.get_description() == 'FDTD+TDDFT': self.hamiltonian.poisson.set_density(self.density) self.hamiltonian.poisson.print_messages(self.text) self.txt.flush() self.initialized = True self._changed_keywords.clear()
from math import sqrt import numpy as np from gpaw.symmetry import Symmetry from ase.dft.kpoints import monkhorst_pack # Primitive diamond lattice, with Si lattice parameter a = 5.475 cell_cv = .5 * a * np.array([(1, 1, 0), (1, 0, 1), (0, 1, 1)]) spos_ac = np.array([(.00, .00, .00), (.25, .25, .25)]) id_a = [1, 1] # Two identical atoms pbc_c = np.ones(3, bool) bzk_kc = monkhorst_pack((4, 4, 4)) # Do check symm = Symmetry(id_a, cell_cv, pbc_c) symm.analyze(spos_ac) ibzk_kc, w_k = symm.reduce(bzk_kc)[:2] assert len(symm.op_scc) == 24 assert len(w_k) == 10 a = 3 / 32. b = 1 / 32. c = 6 / 32. assert np.all(w_k == [a, b, a, c, c, a, a, a, a, b]) assert not symm.op_scc.sum(0).any() # Rotate unit cell and check again: cell_cv = a / sqrt(2) * np.array([(1, 0, 0), (0.5, sqrt(3) / 2, 0), (0.5, sqrt(3) / 6, sqrt(2.0 / 3))]) symm = Symmetry(id_a, cell_cv, pbc_c) symm.analyze(spos_ac)