from copy import deepcopy
from mrsimulator import Simulator
from mrsimulator import SpinSystem
from mrsimulator.methods import BlochDecaySpectrum
from mrinversion.kernel.base import LineShape
[docs]class ShieldingPALineshape(LineShape):
"""
A generalized class for simulating the pure anisotropic NMR nuclear shielding
line-shape kernel.
Args:
anisotropic_dimension: A Dimension object, or an equivalent dictionary
object. This dimension must represent the pure anisotropic
dimension.
inverse_dimension: A list of two Dimension objects, or equivalent
dictionary objects representing the `x`-`y` coordinate grid.
channel: The channel is an isotope symbol of the nuclei given as the atomic
number followed by the atomic symbol, for example, `1H`, `13C`, and
`29Si`. This nucleus must correspond to the recorded frequency
resonances.
magnetic_flux_density: The magnetic flux density of the external static
magnetic field. The default value is 9.4 T.
rotor_angle: The angle of the sample holder (rotor) relative to the
direction of the external magnetic field. The default value is
54.735 deg (magic angle).
rotor_frequency: The effective sample spin rate. Depending on the NMR
sequence, this value may be less than the physical sample rotation
frequency. The default is 14 kHz.
number_of_sidebands: The number of sidebands to simulate along the
anisotropic dimension. The default value is 1.
"""
def __init__(
self,
anisotropic_dimension,
inverse_dimension,
channel,
magnetic_flux_density="9.4 T",
rotor_angle="54.735 deg",
rotor_frequency="14 kHz",
number_of_sidebands=1,
):
super().__init__(
anisotropic_dimension,
inverse_dimension,
channel,
magnetic_flux_density,
rotor_angle,
rotor_frequency,
number_of_sidebands,
)
[docs] def kernel(self, supersampling=1):
"""
Return the NMR nuclear shielding anisotropic line-shape kernel.
Args:
supersampling: An integer. Each cell is supersampled by the factor
`supersampling` along every dimension.
Returns:
A numpy array containing the line-shape kernel.
"""
args_ = deepcopy(self.method_args)
method = BlochDecaySpectrum.parse_dict_with_units(args_)
isotope = args_["channels"][0]
zeta, eta = self._get_zeta_eta(supersampling)
x_csdm = self.inverse_kernel_dimension[0]
if x_csdm.coordinates.unit.physical_type == "frequency":
# convert zeta to ppm if given in frequency units.
zeta /= self.larmor_frequency # zeta in ppm
for dim_i in self.inverse_kernel_dimension:
if dim_i.origin_offset.value == 0:
dim_i.origin_offset = f"{abs(self.larmor_frequency)} MHz"
spin_systems = [
SpinSystem(
sites=[dict(isotope=isotope, shielding_symmetric=dict(zeta=z, eta=e))]
)
for z, e in zip(zeta, eta)
]
sim = Simulator()
sim.config.number_of_sidebands = self.number_of_sidebands
sim.config.decompose_spectrum = "spin_system"
sim.spin_systems = spin_systems
sim.methods = [method]
sim.run(pack_as_csdm=False)
amp = sim.methods[0].simulation.real
return self._averaged_kernel(amp, supersampling)
[docs]class MAF(ShieldingPALineshape):
r"""A specialized class for simulating the pure anisotropic NMR nuclear shielding
line-shape kernel resulting from the 2D MAF spectra.
Args:
anisotropic_dimension: A Dimension object, or an equivalent dictionary
object. This dimension must represent the pure anisotropic
dimension.
inverse_dimension: A list of two Dimension objects, or equivalent
dictionary objects representing the `x`-`y` coordinate grid.
channel: The isotope symbol of the nuclei given as the atomic number
followed by the atomic symbol, for example, `1H`, `13C`, and
`29Si`. This nucleus must correspond to the recorded frequency
resonances.
magnetic_flux_density: The magnetic flux density of the external static
magnetic field. The default value is 9.4 T.
**Assumptions:**
The simulated line-shapes correspond to an infinite speed spectrum spinning at
:math:`90^\circ`.
"""
def __init__(
self,
anisotropic_dimension,
inverse_dimension,
channel,
magnetic_flux_density="9.4 T",
):
super().__init__(
anisotropic_dimension,
inverse_dimension,
channel,
magnetic_flux_density,
"90 deg",
"200 GHz",
1,
)
[docs]class SpinningSidebands(ShieldingPALineshape):
r"""A specialized class for simulating the pure anisotropic spinning sideband
amplitudes of the nuclear shielding resonances resulting from a 2D sideband
separation spectra.
Args:
anisotropic_dimension: A Dimension object, or an equivalent dictionary
object. This dimension must represent the pure anisotropic
dimension.
inverse_dimension: A list of two Dimension objects, or equivalent
dictionary objects representing the `x`-`y` coordinate grid.
channel: The isotope symbol of the nuclei given as the atomic number
followed by the atomic symbol, for example, `1H`, `13C`, and
`29Si`. This nucleus must correspond to the recorded frequency
resonances.
magnetic_flux_density: The magnetic flux density of the external static
magnetic field. The default value is 9.4 T.
**Assumption:**
The simulated line-shapes correspond to a finite speed spectrum spinning at the
magic angle, :math:`54.735^\circ`, where the spin rate is the increment along
the anisotropic dimension.
"""
def __init__(
self,
anisotropic_dimension,
inverse_dimension,
channel,
magnetic_flux_density="9.4 T",
):
super().__init__(
anisotropic_dimension,
inverse_dimension,
channel,
magnetic_flux_density,
"54.735 deg",
None,
None,
)
# class DAS(LineShape):
# def __init__(
# self,
# anisotropic_dimension,
# inverse_kernel_dimension,
# channel,
# magnetic_flux_density="9.4 T",
# rotor_angle="54.735 deg",
# rotor_frequency="600 Hz",
# number_of_sidebands=None,
# ):
# super().__init__(
# anisotropic_dimension,
# inverse_kernel_dimension,
# channel,
# magnetic_flux_density,
# rotor_angle,
# rotor_frequency,
# number_of_sidebands,
# # "DAS",
# )
# def kernel(self, supersampling):
# method = BlochDecayCentralTransitionSpectrum.parse_dict_with_units(
# self.method_args
# )
# isotope = self.method_args["channels"][0]
# Cq, eta = self._get_zeta_eta(supersampling)
# spin_systems = [
# SpinSystem(sites=[dict(isotope=isotope, quadrupolar=dict(Cq=cq_, eta=e))])
# for cq_, e in zip(Cq, eta)
# ]
# self.simulator.spin_systems = spin_systems
# self.simulator.methods = [method]
# self.simulator.run(pack_as_csdm=False)
# amp = self.simulator.methods[0].simulation
# return self._averaged_kernel(amp, supersampling)