#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
from dataclasses import dataclass, field
from mrfmsim.component import ComponentBase
spin_dict = {
# note: not accounting for the g-factor of the electron spin;
"e": {"Gamma": 1.760859708e8, "J": 0.5}, # rad/s.mT
"1H": {"Gamma": 2.675222005e05, "J": 0.5}, # rad/s.mT
"71Ga": {"Gamma": 2.0 * np.pi * 12.98e3, "J": 1.5}, # rad/s.mT
"19F": {"Gamma": 2.0 * np.pi * 40.05e3, "J": 0.5}, # rad/s.mT
"2H": {"Gamma": 2.0 * np.pi * 6.536e3, "J": 1.0}, # rad/s.mT
}
[docs]@dataclass
class Sample(ComponentBase):
r"""Sample object for magnetic resonance force microscopy experiments.
For spins 'e', '1H', '71Ga', '19F', or '2H' the Gamma and J are preset.
For other spins, input the name, Gamma and J directly.
:param str spin: spin type
:param float T1: spin-lattice relaxation :math:`T_1` [s]
:param float T2: spin-spin relaxation :math:`T_2` [s]
:param float temperature: the sample temperature [K]
:param float spin_density: the sample spin density :math:`\rho` [1/nm^3]
:param float Gamma: spin gyromagnetic ratio [rad/s.mT]
defaults to None if spin_type is one of the preset
:param float L: spin angular momentum [unitless]
default to None if spin_type is one of the preset
:param float dB_hom: homogeneous linewidth [mT]
:param float dB_sat: saturation linewidth [mT]
"""
spin: str
T1: float = field(metadata={"unit": "s", "format": ".3e"})
T2: float = field(metadata={"unit": "s", "format": ".3e"})
temperature: float = field(metadata={"unit": "K"})
spin_density: float = field(metadata={"unit": "1/nm^3"})
Gamma: float = field(default=None, metadata={"unit": "rad/(s.mT)", "format": ".3e"})
J: float = field(default=None, metadata={"format": ".1f"})
dB_hom: float = field(init=False, default=None, metadata={"unit": "mT"})
dB_sat: float = field(init=False, default=None, metadata={"unit": "mT"})
def __post_init__(self):
self.Gamma = self.Gamma or spin_dict[self.spin]["Gamma"] # rad/s.mT
self.J = self.J or spin_dict[self.spin]["J"]
self.dB_hom = 1 / (self.Gamma * self.T2) # mT
self.dB_sat = 1 / (self.Gamma * np.sqrt(self.T1 * self.T2)) # mT
# class Sample:
# r"""Sample object for magnetic resonance force microscopy experiments.
# The **Sample** carries the properties of the nuclear or electron
# spins in the sample.
# The default spin type includes the properties of
# 'electron', '1H', '2H', '19F', '71Ga'. For spin that is not included
# simply provide a name for spin, gamma, and j.
# :cvar float hbar: reduced Plank constant, unit [aN nm s]
# :cvar float kB: Boltzmann constant [aN nm k:math:'^{-1}']
# """
# hbar = 1.054571628e-7 # aN nm s - reduced Plank constant
# kB = 1.3806504e4 # aN nm K^{-1} - Boltzmann constant
# _spintype = {
# # note: not accounting for the g-factor of the electron spin;
# "electron": {"Gamma": 1.760859708e8, "J": 0.5}, # rad/s.mT
# "1H": {"Gamma": 2.675222005e05, "J": 0.5}, # rad/s.mT
# "71Ga": {"Gamma": 2.0 * np.pi * 12.98e3, "J": 1.5}, # rad/s.mT
# "19F": {"Gamma": 2.0 * np.pi * 40.05e3, "J": 0.5}, # rad/s.mT
# "2H": {"Gamma": 2.0 * np.pi * 6.536e3, "J": 1.0}, # rad/s.mT
# }
# def __init__(
# self, spin_type, T1, T2, temperature, spin_density, Gamma=None, J=None
# ):
# r"""Initialize sample object.
# The values for the gyromagnetic ratio and total spin angular momentum
# are populated automatically based on the nucleus.
# The homogeneous linewidth ``dB_hom`` is defined as
# .. math::
# \Delta B_{\text{homog}} = \dfrac{1}{\gamma T_2}.
# The saturation linewidth ``dB_sat`` is defined as
# .. math::
# \Delta B_{\text{sat}} = \dfrac{1}{\gamma \sqrt{T_1 \: T_2}}.
# The variance in a single spin's magnetization in the low-polarization limit (mz2_eq)
# is given by [#Xue]_
# .. math::
# \sigma_{{\cal M}_{z}}^{2} = \hbar^2 \gamma^2 \dfrac{J \: (J + 1)}{3}
# :param str spin_type: 'e', '1H', '71Ga', '19F', or '2H'
# :param float T1: the spin-lattice relaxation time :math:`T_1` [s]
# :param float T2: spin dephasing time :math:`T_2` [s]
# :param float spin_density: the sample spin
# density :math:`\rho` [1/nm^3]; *note the units*
# :param float Gamma: spin gyromagnetic ratio [rad/s.mT]
# defaults to None if spin_type is one of the preset
# :param float J: total spin angular momentum [unitless]
# default to None if spin_type is one of the preset
# :ivar float Gamma: gyromagnetic ratio [rad/s.mT]
# :ivar float dB_hom: the homogeneous linewidth [mT]
# :ivar float dB_sat: the saturation linewidth [mT]
# .. [#Xue] Equations 1 and 2 in Xue, F.; Weber, D.; Peddibhotla, P. & Poggio, M.
# "Measurement of statistical nuclear spin polarization in a nanoscale GaAs sample",
# *Phys. Rev. B*, **2011**, *84*, 205328
# [`10.1103/PhysRevB.84.205328 <http://dx.doi.org/10.1103/PhysRevB.84.205328>`__].
# """
# self.spin_type = spin_type
# self.Gamma = Gamma or self._spintype[spin_type]["Gamma"] # rad/s.mT
# self.J = J or self._spintype[spin_type]["J"]
# self.mu_z = self.hbar * self.Gamma * self.J
# self.T1 = T1
# self.T2 = T2
# self.spin_density = spin_density
# self.dB_hom = 1 / (self.Gamma * self.T2) # mT
# self.dB_sat = 1 / (self.Gamma * np.sqrt(self.T1 * self.T2)) # mT
# self.f_larmor = self.Gamma * 1e3 / (2 * np.pi)
# self.temperature = temperature
# # calculate the mz2_eq
# self.mz2_eq = (
# self.hbar * self.Gamma * np.sqrt(self.J * (self.J + 1) / 3.0)
# ) ** 2 # (aN.nm/mT)^2
# unit: str = field(init=False, default=None)
# def __post_init__(self):
# self.registry = get_unit_registry(**asdict(self))
# if self.registry is not None:
# self.unit = self.registry.default_system