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