.. _getting_started:
===============
Getting Started
===============
In mrsimulator, the user initializes objects from mrsimulator classes;
The three main classes we will use in this example are:
:ref:`spin_system_documentation`, :ref:`method_documentation`, and
:ref:`simulator_documentation`.
SpinSystem defines the
spin system and its tensor parameters used to generate a particular
subspectrum, and Method defines the behavior and parameters
for the particular NMR measurement to be simulated. A list
of Method and SpinSystem objects are used to initialize a Simulator
object, which is then used to generate the corresponding NMR spectra---returned
as a CSDM object in each Method object. For more information on the CSDM
(Core Scientific Dataset Model), see the `csdmpy documentation
`__. There is an additional class,
:ref:`signal_processor_documentation`, for applying various post-simulation
signal processing operations to CSDM dataset objects.
All objects in **mrsimulator** can be
serialized. We adopt the `Javascript Object Notation
(JSON) `__ as the file-serialization format for the
model because it is human-readable if properly organized and easily integrable
with numerous programming languages and related software packages. It is also
the preferred serialization for data exchange in web-based applications.
Here, we have put together a tutorial which introduces the key objects in
a typical **mrsimulator** workflow. See the User Documentation section
for more detailed documentation on the usage of **mrsimulator** classes. Also,
check out our :ref:`example_gallery` and :ref:`fitting_examples`.
SpinSystem
----------
An NMR spin system is an isolated system of sites (spins) and couplings. Spin
systems can include as many sites and couplings as necessary to model a sample.
For this introductory example, you will create a coupled
:math:`^1\text{H}` - :math:`^{13}\text{C}` spin system. Use the code below to
construct two :ref:`site_documentation` objects for the :math:`^1\text{H}`
and :math:`^{13}\text{C}` sites.
.. plot::
:context: reset
# Import the Site and SymmetricTensor classes
from mrsimulator import Site
from mrsimulator.spin_system.tensors import SymmetricTensor
# Create the Site objects
H_site = Site(isotope="1H")
C_site = Site(
isotope="13C",
isotropic_chemical_shift=100.0, # in ppm
shielding_symmetric=SymmetricTensor(
zeta=70.0, # in ppm
eta=0.5,
),
)
my_sites = [H_site, C_site]
Note that isotopes in **mrsimulator** are specified with a string that starts
with the isotope's mass number followed by its element symbol.
In the code above, you created two Site objects in the variables
``H_site`` and ``C_site``. The ``H_site`` variable represents a proton site with a
(default) chemical shift of zero. The ``C_site`` variable represents a
carbon-13 site with a chemical shift of 100 ppm and a shielding
component represented by a :ref:`sy_api` object. We parametrize tensors using
the Haeberlen convention. All spin interaction parameters, e.g., isotropic
chemical shift and other coupling parameters, are initialized to zero by
default. Additionally, the default Site isotope is ``1H``.
At the end of the code above, you placed ``H_site`` and ``C_site`` into a
Python list named ``my_sites``. The order of Sites in this list is important,
as the indexes of Sites in this list are used when specifying couplings between sites.
Note that indexes in Python start at zero.
Using the code below, define a dipolar coupling between ``H_site`` and ``C_site``
by creating a :ref:`coupling_documentation` object.
.. plot::
:context: close-figs
# Import the Coupling class
from mrsimulator import Coupling
# Create the Coupling object
coupling = Coupling(
site_index=[0, 1],
dipolar=SymmetricTensor(D=-2e4), # in Hz
)
The two sites involved in the Coupling are identified by their indexes in the list
variable ``site_index``.
Now you have all the pieces needed to create the spin system using the code below.
.. plot::
:context: close-figs
# Import the SpinSystem class
from mrsimulator import SpinSystem
# Create the SpinSystem object
spin_system = SpinSystem(
sites = my_sites,
couplings=[coupling],
)
That's it! You have created a spin system whose spectrum is ready to be simulated.
If you had wanted to create an uncoupled spin system, simply omit the
``couplings`` attribute.
Method
------
A Method object in **mrsimulator** describes an NMR method.
For this introduction, you can use the pre-defined
method :py:class:`~mrsimulator.method.lib.BlochDecaySpectrum`. This method
simulations the spectrum obtained from the Fourier transform of a Bloch decay
signal, i.e., one-pulse and acquire. You can use the code below to create
the Method object initialized with attributes whose names should be relatively
familiar to an NMR spectroscopist.
.. plot::
:context: close-figs
# Import the BlochDecaySpectrum class
from mrsimulator.method.lib import BlochDecaySpectrum
from mrsimulator.method import SpectralDimension
# Create a BlochDecaySpectrum object
method = BlochDecaySpectrum(
channels=["13C"],
magnetic_flux_density=9.4, # in T
rotor_angle=54.735 * 3.14159 / 180, # in rad (magic angle)
rotor_frequency=3000, # in Hz
spectral_dimensions=[
SpectralDimension(
count=2048,
spectral_width=80e3, # in Hz
reference_offset=6e3, # in Hz
label=r"$^{13}$C resonances",
)
],
)
The ``channel`` attribute holds a list of isotope strings. In the
BlochDecaySpectrum method, however, only the
first isotope in the list, i.e., :math:`^{13}\text{C}`, is used to simulate
the spectrum. The BlochDecaySpectrum method has one spectral
dimension. In this example, that spectral dimension has 2048 points, spanning
80 kHz with a reference offset of 6 kHz.
Next, you will bring the SpinSystem and Method objects together and create a Simulator object
that will simulate the spectrum.
Simulator
---------
At the heart of **mrsimulator** is the Simulator object, which
calculates the NMR spectrum. **Mrsimulator** performs all calculations in the frequency domain,
and all resonance frequencies are calculated in the weakly-coupled (Zeeman) basis for the spin system.
In the code below, you create a Simulator object,
initialized with your previously defined spin system and method, and then call
:py:meth:`~mrsimulator.Simulator.run` on your Simulator object.
.. plot::
:context: close-figs
# Import the Simulator class
from mrsimulator import Simulator
# Create a Simulator object
sim = Simulator(spin_systems=[spin_system], methods=[method])
sim.run()
The simulated spectrum is stored as a CSDM object in the Method object at
``sim.methods[0].simulation``. To match an experimental MAS spectrum, however,
you still need to add some line broadening to the simulated spectrum. For this,
you can use the :ref:`signal_processor_documentation` object described in the
next section.
SignalProcessor
---------------
A :ref:`signal_processor_api` object holds a list of operations applied
sequentially to a dataset. For a comprehensive list of operations and further
details on using the SignalProcessor object, consult
the :ref:`signal_processor_documentation` documentation.
Use the code below to create a SignalProcessor object that performs a
convolution of the simulated spectrum with a Lorentzian distribution having a
full-width-half-maximum of 200 Hz. This is done with three operations: the
first operation applies an inverse fast Fourier transform of the spectrum into
the time domain, the second operation applies a time-domain apodization with an
exponential decay, and the third operation applies a fast Fourier transform
back into the frequency domain.
.. plot::
:context: close-figs
from mrsimulator import signal_processor as sp
# Create the SignalProcessor object
processor = sp.SignalProcessor(
operations=[
sp.IFFT(),
sp.apodization.Exponential(FWHM="200 Hz"),
sp.FFT(),
]
)
# Apply the processor to the simulation dataset
processed_simulation = processor.apply_operations(dataset=sim.methods[0].simulation)
PyPlot
------
You can use Matplotlib's `PyPlot module
`__ to plot your
simulations. To aid in plotting CSDM objects with PyPlot, csdmpy provides a
custom CSDM dataset plot axes. To use it, simply pass ``projection="csdm"`` when instantiating
an Axes instance. Below is code using the PyPlot module which will generate a
plot and a pdf file of the simulated spectrum:
.. note::
To use the custom CSDM axes with ``projection="csdm"``, the csdmpy library needs imported.
.. _fig1-getting-started:
.. skip: next
.. plot::
:context: close-figs
:caption: A simulated :math:`^{13}\text{C}` MAS spectrum.
import matplotlib.pyplot as plt
plt.rcParams['pdf.fonttype'] = 42 # For using plots in Illustrator
plt.figure(figsize=(5, 3)) # set the figure size
ax = plt.subplot(projection="csdm")
ax.plot(processed_simulation.real)
ax.invert_xaxis() # reverse x-axis
plt.tight_layout()
plt.savefig("spectrum.pdf")
plt.show()
The ``plt.savefig("spectrum.pdf")`` line creates a pdf file that can be edited
in a vector graphics editor such as Adobe Illustrator. We encourage you to
work through the `PyPlot basic usage tutorial
`__
to understand its methods and learn how to further customize your plots.
CSDM
----
**Mrsimulator** is designed to be part of a larger data workflow involving other
software packages. For this larger context, **mrsimulator** uses the Core
Scientific Dataset Model (CSDM) for importing and exporting your datasets. CSDM
is a lightweight, portable, human-readable, and versatile standard for intra-
and interdisciplinary exchange of scientific datasets. The model supports
multi-dimensional datasets with a multi-component dependent variable discretely
sampled at unique points in a multi-dimensional independent variable space. It
can also hold correlated datasets assuming the different physical quantities
(dependent variables) are sampled on the same orthogonal grid of independent
variables. It can even handle datasets with non-uniform sampling on a grid.
The CSDM can also serve as a re-usable building block in developing
more sophisticated portable scientific dataset file standards.
**Mrsimulator** also uses CSDM internally as its object model for simulated and
experimental datasets. Any CSDM object in **mrsimulator** can be serialized as
a JavaScript Object Notation (JSON) file using its ``save()`` method. For
example, the simulation after the signal processing step above is saved as a
csdf file as shown below.
.. plot::
:context: close-figs
processed_simulation.save("processed_simulation.csdf")
For more information on the CSDM file formats, see the `csdmpy documentation `__.
.. plot::
:include-source: False
import os
from os.path import isfile
if isfile("spectrum.pdf"): os.remove("spectrum.pdf")
if isfile("processed_simulation.csdf"): os.remove("processed_simulation.csdf")