def spin(elem='mu', iso=None): """Return the spin of a given particle Return the intrinsic angular momentum of either an atomic nucleus, an electron or a muon. The value returned is in hbar. Keyword Arguments: elem {str} -- Element ('e' for electron, 'mu' for muon) (default: {'mu'}) iso {int} -- Desired isotope. Ignored for 'mu'. If used for 'e', this is interpreted as a number of strongly coupled electrons acting as a single spin > 1/2. If not specified, the most naturally abundant isotope is used. (default: {None}) """ if elem == 'mu': return 0.5 elif elem == 'e': iso = iso or 1 if iso < 1 or int(iso) != iso: raise ValueError('Invalid multiplicity ' '{0} for electron'.format(iso)) return 0.5 * int(iso) else: try: val = _get_isotope_data([elem], 'I', isotope_list=[iso])[0] except RuntimeError: raise ValueError('Invalid isotope {0} for element {1}'.format( iso, elem)) return val
def gyromagnetic_ratio(elem='mu', iso=None): """Return the gyromagnetic ratio of a given particle Return the gyromagnetic ratio of either an atomic nucleus, an electron or a muon. The value returned is in MHz/T. It corresponds to a frequency, not a pulsation (so it's gamma, not gammabar = 2*pi*gamma). Keyword Arguments: elem {str} -- Element ('e' for electron, 'mu' for muon) (default: {'mu'}) iso {int} -- Desired isotope. Ignored for 'e' and 'mu'. If not specified, the most naturally abundant isotope is used. (default: {None}) """ if elem == 'e': return ELEC_GAMMA elif elem == 'mu': return MU_GAMMA else: try: val = _get_isotope_data([elem], 'gamma', isotope_list=[iso])[0] except RuntimeError: raise ValueError('Invalid isotope {0} for element {1}'.format( iso, elem)) return val / (2e6 * np.pi)
def quadrupole_moment(elem='mu', iso=None): """Return the quadrupole moment of a given particle Return the quadrupole moment of either an atomic nucleus, an electron or a muon. The value returned is in barn. Keyword Arguments: elem {str} -- Element ('e' for electron, 'mu' for muon) (default: {'mu'}) iso {int} -- Desired isotope. Ignored for 'e' and 'mu'. If not specified, the most naturally abundant isotope is used. (default: {None}) """ if elem in ('e', 'mu'): return 0 else: try: val = _get_isotope_data([elem], 'Q', isotope_list=[iso])[0] except RuntimeError: raise ValueError('Invalid isotope {0} for element {1}'.format( iso, elem)) return val
def compute_hfine_tensor( points, spins, cell=None, self_i=0, species="e", cut_r=10, lorentz=True, fermi_mm=0, ): """Compute the hyperfine tensor experienced at point of index i generated by a number of localised spins at points, for a given periodic unit cell and species. | Args: | points (np.ndarray): coordinates of points at which the spins are | localised | spins (np.ndarray): magnetic moments (as spin quantum number, e.g. | 0.5 for an electron or 1H nucleus with spin up) | cell (np.ndarray): unit cell (if None, considered non-periodic) | self_i (int): index of point at which to compute the tensor. | Local spin density will give rise to a Fermi | contact term | species (str or [str]): symbol or list of symbols identifying the | species generating the magnetic field. | Determines the magnetic moments | cut_r (float): cutoff radius for dipolar component calculation | lorentz (bool): if True, include a Lorentz term (average bulk | magnetization). Default is True | fermi_mm (float): Magnetic moment density at site i to use for | computation of the Fermi contact term. Units | are Bohr magnetons/Ang^3 | Returns: | HT (np.ndarray): hyperfine tensor at point i """ N = len(points) magmoms = np.array(spins).astype(float) species = np.array(species).astype("S2") if species.shape == (): species = np.repeat(species[None], N) for i, s in enumerate(species): if s == b"e": mm = 2 * _bohrmag elif s == b"mu": mm = mu_cnst.m_gamma * cnst.hbar else: mm = _get_isotope_data(s, "gamma")[0] * cnst.hbar magmoms[i] *= mm * abs(cnst.physical_constants["electron g factor"][0]) # Do we need a supercell? r = np.array(points) - points[self_i] if cell is not None: scell = minimum_supcell(cut_r, latt_cart=cell) fxyz, xyz = supcell_gridgen(cell, scell) r = r[:, None, :] + xyz[None, :, :] else: r = r[:, None, :] rnorm = np.linalg.norm(r, axis=-1) # Expunge the ones that are outside of the sphere sphere = np.where(rnorm <= cut_r) r = r[sphere] rnorm = rnorm[sphere] magmoms = magmoms[sphere[0]] # Find the contact point self_i = np.argmin(rnorm) magmoms[self_i] = 0 rnorm_inv = 1.0 / np.where(rnorm > 0, rnorm, np.inf) rdyad = r[:, None, :] * r[:, :, None] rdip = 3 * rdyad * rnorm_inv[:, None, None] ** 2 - np.eye(3)[None, :, :] HT = np.sum(magmoms[:, None, None] * rdip * rnorm_inv[:, None, None] ** 3, axis=0) HT *= cnst.mu_0 / (4 * np.pi) * 1e30 # Add Lorentz term if cell is not None and lorentz: avgM = np.sum(magmoms) * 3.0 / (4.0 * np.pi * cut_r ** 3) HT += np.eye(3) * avgM * cnst.mu_0 / 3.0 * 1e30 # Add contact term if fermi_mm: fermi_mm *= _bohrmag * abs(cnst.physical_constants["electron g factor"][0]) HT += np.eye(3) * fermi_mm * 2.0 / 3.0 * cnst.mu_0 * 1e30 return HT
def __init__( self, atoms, mu_pos, isotopes={}, isotope_list=None, cutoff=10, overlap_eps=1e-3, ): # Get positions, cell, and species, only things we care about self.cell = np.array(atoms.get_cell()) pos = atoms.get_positions() el = np.array(atoms.get_chemical_symbols()) self.mu_pos = np.array(mu_pos) # Is it periodic? if np.any(atoms.get_pbc()): scell = minimum_supcell(cutoff, self.cell) else: scell = [1, 1, 1] grid_f, grid = supcell_gridgen(self.cell, scell) self.grid_f = grid_f r = (pos[:, None, :] + grid[None, :, :] - self.mu_pos[None, None, :]).reshape((-1, 3)) rnorm = np.linalg.norm(r, axis=1) sphere = np.where(rnorm <= cutoff)[0] sphere = sphere[np.argsort(rnorm[sphere])[::-1]] # Sort by length r = r[sphere] rnorm = rnorm[sphere] self._r = r self._rn = rnorm self._rn = np.where(self._rn > overlap_eps, self._rn, np.inf) self._ri = sphere self._dT = (3 * r[:, :, None] * r[:, None, :] / self._rn[:, None, None]**2 - np.eye(3)[None, :, :]) / 2 self._an = len(pos) self._gn = self.grid_f.shape[0] self._a_i = self._ri // self._gn self._ijk = self.grid_f[self._ri % self._gn] # Get gammas self.gammas = _get_isotope_data(el, "gamma", isotopes, isotope_list) self.gammas = self.gammas[self._a_i] Dn = _dip_constant(self._rn * 1e-10, m_gamma, self.gammas) De = _dip_constant( self._rn * 1e-10, m_gamma, cnst.physical_constants["electron gyromag. ratio"][0], ) self._D = {"n": Dn, "e": De} # Start with all zeros self.spins = rnorm * 0