示例#1
0
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
示例#2
0
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)
示例#3
0
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
示例#5
0
    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