def psi(self, r, m=0): r""" Calculate :math:`\phi(\mathbf R)` at a given point (or more points) The position `r` is a vector from the origin of this orbital. Parameters ----------- r : array_like of (:, 3) the vector from the orbital origin m : int, optional magnetic quantum number, must be in range ``-self.l <= m <= self.l`` Returns ------- numpy.ndarray basis function value at point `r` """ r = _a.asarrayd(r) s = r.shape[:-1] # Convert to spherical coordinates n, idx, r, theta, phi = cart2spher(r, theta=m != 0, cos_phi=True, maxR=self.R) p = _a.zerosd(n) if len(idx) > 0: p[idx] = self.psi_spher(r, theta, phi, m, cos_phi=True) # Reduce memory immediately del idx, r, theta, phi p.shape = s return p
def test_spherical(): rad2 = np.pi / 45 r, theta, phi = np.ogrid[0.1:10:0.2, -np.pi:np.pi:rad2, 0:np.pi:rad2] xyz = spher2cart(r, theta, phi) s = xyz.shape[:-1] r1, theta1, phi1 = cart2spher(xyz) r1.shape = s theta1.shape = s phi1.shape = s assert np.allclose(r, r1) assert np.allclose(theta, theta1[1:2, :, 1:2]) assert np.allclose(phi, phi1[0:1, 0:1, :])
def _call(self, *args, **kwargs): data_axis = kwargs.pop('data_axis', None) grid_unit = kwargs.pop('grid_unit', 'b') func = getattr(self.parent, self._bz_attr) wrap = allow_kwargs('parent', 'k', 'weight')(kwargs.pop('wrap', _do_nothing)) eta = tqdm_eta(len(self), self.__class__.__name__ + '.asgrid', 'k', kwargs.pop('eta', False)) parent = self.parent k = self.k w = self.weight # Extract information from the MP grid, these values # define the Grid size, etc. diag = self._diag.copy() if not np.all(self._displ == 0): raise SislError(self.__class__.__name__ + '.{} requires the displacement to be 0 for all k-points.'.format(self._bz_attr)) displ = self._displ.copy() size = self._size.copy() steps = size / diag if self._centered: offset = np.where(diag % 2 == 0, steps, steps / 2) else: offset = np.where(diag % 2 == 0, steps / 2, steps) # Instead of doing # _in_primitive(k) + 0.5 - offset # we can do it here # _in_primitive(k) + offset' offset -= 0.5 # Check the TRS direction trs_axis = self._trs _in_primitive = self.in_primitive _rint = np.rint _int32 = np.int32 def k2idx(k): # In case TRS is applied two indices may be returned return _rint((_in_primitive(k) - offset) / steps).astype(_int32) # To find the opposite k-point, do this # idx[i] = [diag[i] - idx[i] - 1, idx[i] # with i in [0, 1, 2] # Create cell from the reciprocal cell. if grid_unit == 'b': cell = np.diag(self._size) else: cell = parent.sc.rcell * self._size.reshape(1, -1) / units('Ang', grid_unit) # Find the grid origo origo = -(cell * 0.5).sum(0) # Calculate first k-point (to get size and dtype) v = wrap(func(*args, k=k[0], **kwargs), parent=parent, k=k[0], weight=w[0]) if data_axis is None: if v.size != 1: raise SislError(self.__class__.__name__ + '.{} requires one value per-kpoint because of the 3D grid values'.format(self._bz_attr)) else: # Check the weights weights = self.grid(diag[data_axis], displ[data_axis], size[data_axis], centered=self._centered, trs=trs_axis == data_axis)[1] # Correct the Grid size diag[data_axis] = len(v) # Create the orthogonal cell direction to ensure it is orthogonal # Since array axis is cyclic for negative numbers, we simply do this cell[data_axis, :] = cross(cell[data_axis-1, :], cell[data_axis-2, :]) # Check whether we should rotate it if cart2spher(cell[data_axis, :])[2] > pi / 4: cell[data_axis, :] *= -1 # Correct cell for the grid if trs_axis >= 0: origo[trs_axis] = 0. # Correct offset since we only have the positive halve if self._diag[trs_axis] % 2 == 0 and not self._centered: offset[trs_axis] = steps[trs_axis] / 2 else: offset[trs_axis] = 0. # Find number of points if trs_axis != data_axis: diag[trs_axis] = len(self.grid(diag[trs_axis], displ[trs_axis], size[trs_axis], centered=self._centered, trs=True)[1]) # Create the grid in the reciprocal cell sc = SuperCell(cell, origo=origo) grid = Grid(diag, sc=sc, dtype=v.dtype) if data_axis is None: grid[k2idx(k[0])] = v else: idx = k2idx(k[0]).tolist() weight = weights[idx[data_axis]] idx[data_axis] = slice(None) grid[idx] = v * weight del v # Now perform calculation if data_axis is None: for i in range(1, len(k)): grid[k2idx(k[i])] = wrap(func(*args, k=k[i], **kwargs), parent=parent, k=k[i], weight=w[i]) eta.update() else: for i in range(1, len(k)): idx = k2idx(k[i]).tolist() weight = weights[idx[data_axis]] idx[data_axis] = slice(None) grid[idx] = wrap(func(*args, k=k[i], **kwargs), parent=parent, k=k[i], weight=w[i]) * weight eta.update() eta.close() return grid
def param_circle(self, sc, N_or_dk, kR, normal, origo, loop=False): r""" Create a parameterized k-point list where the k-points are generated on a circle around an origo The generated circle is a perfect circle in the reciprocal space (Cartesian coordinates). To generate a perfect circle in units of the reciprocal lattice vectors one can generate the circle for a diagonal supercell with side-length :math:`2\pi`, see example below. Parameters ---------- sc : SuperCell, or SuperCellChild the supercell used to construct the k-points N_or_dk : int number of k-points generated using the parameterization (if an integer), otherwise it specifies the discretization length on the circle (in 1/Ang), If the latter case will use less than 4 points a warning will be raised and the number of points increased to 4. kR : float radius of the k-point. In 1/Ang normal : array_like of float normal vector to determine the circle plane origo : array_like of float origo of the circle used to generate the circular parameterization loop : bool, optional whether the first and last point are equal Examples -------- >>> sc = SuperCell([1, 1, 10, 90, 90, 60]) >>> bz = BrillouinZone.param_circle(sc, 10, 0.05, [0, 0, 1], [1./3, 2./3, 0]) To generate a circular set of k-points in reduced coordinates (reciprocal >>> sc = SuperCell([1, 1, 10, 90, 90, 60]) >>> bz = BrillouinZone.param_circle(sc, 10, 0.05, [0, 0, 1], [1./3, 2./3, 0]) >>> bz_rec = BrillouinZone.param_circle(2*np.pi, 10, 0.05, [0, 0, 1], [1./3, 2./3, 0]) >>> bz.k[:, :] = bz_rec.k[:, :] Returns ------- BrillouinZone : with the parameterized k-points. """ if isinstance(N_or_dk, Integral): N = N_or_dk else: # Calculate the required number of points N = int(kR ** 2 * np.pi / N_or_dk + 0.5) if N < 4: N = 4 info('BrillouinZone.param_circle increased the number of circle points to 4.') # Conversion object bz = BrillouinZone(sc) normal = _a.arrayd(normal) origo = _a.arrayd(origo) k_n = bz.tocartesian(normal) k_o = bz.tocartesian(origo) # Generate a preset list of k-points on the unit-circle if loop: radians = _a.aranged(N) / (N-1) * 2 * np.pi else: radians = _a.aranged(N) / N * 2 * np.pi k = _a.emptyd([N, 3]) k[:, 0] = np.cos(radians) k[:, 1] = np.sin(radians) k[:, 2] = 0. # Now generate the rotation _, theta, phi = cart2spher(k_n) if theta != 0: pv = _a.arrayd([k_n[0], k_n[1], 0]) pv /= fnorm(pv) q = Quaternion(phi, pv, rad=True) * Quaternion(theta, [0, 0, 1], rad=True) else: q = Quaternion(0., [0, 0, k_n[2] / abs(k_n[2])], rad=True) # Calculate k-points k = q.rotate(k) k *= kR / fnorm(k).reshape(-1, 1) k = bz.toreduced(k + k_o) # The sum of weights is equal to the BZ area W = np.pi * kR ** 2 w = np.repeat([W / N], N) return BrillouinZone(sc, k, w)