MELORDAMA Recoupling Tutorial

If you are going to actually work through this tutorial, please work through examples/jupyter/quick start guide.ipynb first.

The cell below sets the notebook up for use with SpinEvolution. Run it each time after opening this notebook or after restarting the Jupyter kernel. If you are not going to run any other cells, there is no need to run this cell either.

In [50]:
#%matplotlib widget
%matplotlib inline

from plot import spinev, plot, setup_ipynb
setup_ipynb()

opts = '-v0 --verb --prev'

We attepmt to simulate ${\langle}I_{1z}+I_{2z}{\rangle}$ dephasing by the MELODRAMA recoupling pulse sequence as described in Sun et al. (1995), Internuclear distance measurements in solid state nuclear magnetic resonance: Dipolar recoupling via rotor synchronized spin locking, J.Chem.Phys. 102, 702-707.

We start with the main input file melo0, which we copy to melo:

In [51]:
%cp melo0 melo

Now open melo by double-clicking on it in Jupyter Lab File Browser. It will open up in a separate text editor window/tab. Drag this tab down to the middle of the screen and to the left to pace the Editor window side by side with the notebook. Then click on the File Browser tab to collapse the File Browser panel. We will keep this very convenient "split-screen" setup throughtout this whole tutorial, making edits to melo and using it as the main input file for the simulations.

The file used for each simulation can be always inspected by opening the collapsible panel right below the spinev() call. All the changes to this file (with respect to its version used in the pevious simulation) are shown automatically below the collapsible. These are the changes that you need to make (and save) in the Editor to follow through the Tutorial.

If something goes wrong, you can restart the kernel, run the setup cell to set everything up, and then proceed with the Tutorial starting from any place you want after copy-pasting the main input file used for that place from the drop-down panel into the Editor and saving it. Make sure that you do not copy the last line (options_cmd_line).

We are ready for our first simulation now:

In [53]:
spinev('melo',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=5
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            16
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -px -py0 -vm -v0 --verb --prev

2022-03-15T04:43:58.181698 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

The results of the similation look very different from Fig. 2 of the paper.

How can that be?

Let's first check out the pulse sequence:

In [54]:
spinev('melo -pseq',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=5
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            16
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -pseq -px -py0 -vm -v0 --verb --prev
2022-03-15T04:44:18.302951 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

The pulse sequence looks good: we see that it is indeed $R\overline{R}\overline{R}R$, where $R = XYYX$

Let's look at the magnetization path in more detail, observing it after every RF step during the whole evolution time. This is done by using the -oes option and making the pulse sequence zero-dimensional with 10 repeats.

In [55]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=5
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            16
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)10
+ timing(usec)      (melo.pp)x10

2022-03-15T04:44:43.946302 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

And now let's make sure that this suspiciouly wild-looking curve indeed contains all the points from the curve we simulated earlier: we need to be sure that we get identical results using these two different approaches. The first approach was simple 1D pulse sequence; the second – 0D pulse sequence supplemented with the -oes option.

In order to do that, we revert the pulse sequence back to 1D and run the simulation again, but now saving the output under a different name so that it does not overwrite the output of our last simulation (which was set up as 0D+oes), and using this previuos output as a reference on the plot.

This can be done with the help of the options -n<outname>) and -ref<refname>, respectively. We also use --lines and --markers plot options to plot the output as small filled circles, and the reference – as the usual solid line.

To show the changes in the main input file, we use plot option --ref<refname> because the output is saved under a different name.

In [56]:
spinev('melo -ntmp --lines=" ",- --markers=." " -refmelo --ref melo',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=5
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            16
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -ntmp --lines= ,- --markers=.  -refmelo --ref melo -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)x10
+ timing(usec)      (melo.pp)10

2022-03-15T04:45:14.824748 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Indeed, everything is coorect.

Let's try to understand how exactly this "wild" curve comes about. It is a result of several things going on at the same time. There is flipping of the magnetization with every RF pulse. There is dipolar evolution. And there is powder averaging.

Quite obviously, the curve is jagged due to the RF pulses. It would be nice if we could simplify their effects. Recall that each pulse is $\tau_R/4$ long and its power is N times $1/\tau_R$. Which means that its flip angle is N/4 full turns. So if N is a multiple of 4, this jaggedness should dissapear!

Let's try it.

In [57]:
spinev('melo -oes --ref tmp',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=4
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            16
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --ref tmp -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)10
+ timing(usec)      (melo.pp)x10
- N=5
+ N=4

2022-03-15T04:45:59.346968 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Yes!

But now we have another problem. This dephasing curve is obviously incorrect. And this kind of "incorrectness" is usually due to insufficient powder averaging. We need to either increase the number of $(\alpha,\beta)$ angles in the set, or the number of steps in the averaging over $\gamma$, or both. Let's try gamma at first because 16 angles, althouth fast to calculate, may be a very poor choice due to the pulse sequence symmetry.

In [58]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=4
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- n_gamma            16
+ n_gamma            15

2022-03-15T04:46:24.000358 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Looks good for now.

Let's now repeat the 1D vs. 0D+oes comparison for N=4:

In [59]:
spinev('melo -ntmp --lines=" ",- --markers=." " -refmelo --ref melo',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
N=4
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -ntmp --lines= ,- --markers=.  -refmelo --ref melo -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)x10
+ timing(usec)      (melo.pp)10

2022-03-15T04:46:55.131291 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Looks right. But our main problem is still not resolved: N=4 and N=5 curves are dramatically different. Let's look at them on the same plot:

In [60]:
spinev('melo -oes --ref tmp',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --ref tmp -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)10
+ timing(usec)      (melo.pp)x10
- N=4
+ scan_par N/4 5/

2022-03-15T04:47:31.829668 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

The N=4 curve decays much faster than the N=5 one. How is it possible? The only difference in these two simulations is the 25% difference in the RF power. According to Sun et al., MELODRAMA design implies that the dephasing curves for these two cases will be very similar.

To understand this, let's look at the spin dynamics in maximum detail. We will start with removing the powder averaging:

In [61]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        35 50 75
n_gamma            *
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- EulerAngles        rep376
+ EulerAngles        35 50 75
- n_gamma            15
+ n_gamma            *

2022-03-15T04:48:13.932252 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

The dephasing of ${\langle}I_{1z}+I_{2z}{\rangle}$ occurs because each crystallite has a different frequency of this oscillation. And in our case, the frequencies for N=4 and N=5 are indeed very simular, as should be expected for MELODRAMA. The problem then must be in how these frequencies are modulated, i.e. in this jagged pattern produced by the RF.

Let's look then at this modulation in pure form, i.e. without the dipolar interaction.

Option -scheck13 simply suppresses the warning that appears when we attempt to simulate evolution under isotropic Hamiltonian while using MAS.

In [62]:
spinev('melo -oes --markers=. -scheck13',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      *1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x10
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        35 50 75
n_gamma            *
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --markers=. -scheck13 -px -py0 -vm -v0 --verb --prev
- atomic_coords      1.481
+ atomic_coords      *1.481

2022-03-15T04:48:34.430785 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

To see the full detail, zoom in on the first two RF cycles:

In [63]:
spinev('melo -oes --markers=. -scheck13',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      *1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x2
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        35 50 75
n_gamma            *
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --markers=. -scheck13 -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)x10
+ timing(usec)      (melo.pp)x2

2022-03-15T04:48:57.067490 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

This looks correct: Each RF pulse in the N=4 case is a $2\pi$ rotation, while the N=5 pulses are $\pi/2$ rotations whith phases $XYYX$ or $\overline{XYYX}$.

Let's restore the dipolar interaction now:

In [64]:
spinev('melo -oes --markers=.',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      (melo.pp)x2
power(kHz)         *           
phase(deg)         *            
freq_offs(kHz)     *           
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        35 50 75
n_gamma            *
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --markers=. -px -py0 -vm -v0 --verb --prev
- atomic_coords      *1.481
+ atomic_coords      1.481

2022-03-15T04:49:19.609780 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Now, this is the moment of truth.

We see that the RF behaves exactly as specified by the MELODRAMA pulse sequence. But the evolution due to the diploar Hamiltonian restored by this pulse sequence is dramatically different for N=4 and N=5 cases even on the time scale of pulse sequence cycle. Which means that the average Hamiltonians produced by these two cases are very different. Which means that what the paper states about this pulse sequence is wrong.

Now, depending on how your intuition works, there can be many ways to guess was is right then. Make your own story here.

My story was as follows. I was looking at the picture above and marveling at how messed up this $XYYX$ phase cycle is. It is not even cyclic for N=5, i.e. it does not return the magnetization to its original state after one cycle. There certainly must be a better way of doing this kind of recoupling. And then I remembered that Fig. 1 caption of the paper mentioned a modification of this pulse sequence that "partially compensates for rf field inhomogeneity", and that this modification was $X\overline{X}Y\overline{Y}\overline{Y}Y\overline{X}X$. Well, but these $X\overline{X}$ and $Y\overline{Y}$ combinations are exactly what we need to make this phase cycle right! Let's implement it!

Since there are now 32 steps in the RF cycle, it will be quicker and more reliable if we compute it rather than create it in a text editor. So we will use the [text]N macro instead of melo.pp to define the pulse sequence. The macro simply expands to N space-separated copies of text. We will also use the modulo function to compute $\overline{R}$ from $R$, and the ; operator to concatenate $R$ and $\overline{R}$ column vectors into the full supercycle:

In [65]:
spinev('melo -oes --markers=.',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]32)x2
power(kHz)         [0]32          
phase(deg)         [0]32        
freq_offs(kHz)     [0]32       
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(8*spinning_freq)
power_1_1=N*spinning_freq
R=["0 180 90 270 270 90 180 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        35 50 75
n_gamma            *
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --markers=. -px -py0 -vm -v0 --verb --prev
- timing(usec)      (melo.pp)x2
+ timing(usec)      ([0]32)x2
- power(kHz)         *           
- phase(deg)         *            
- freq_offs(kHz)     *           
+ power(kHz)         [0]32          
+ phase(deg)         [0]32        
+ freq_offs(kHz)     [0]32       
- pulse_1_1=1000/(4*spinning_freq)
+ pulse_1_1=1000/(8*spinning_freq)
+ R=["0 180 90 270 270 90 180 0"]'
+ B=mod(R+180,360)   // 'R-bar'
+ phase_1_1 = R; B; B; R

2022-03-15T04:50:26.096488 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Now the N=4 and N=5 cases behave very similarly: our guess was correct!

It remains now to sample the data properly, i.e. once every rotor period or something like that. If we simulate the pulse sequence as 1D, we are limited to sampling it once per full RF cycle, i.e. once every $4\tau_R$. So we'll stick with our 0D+oes approach.

The size of the output matrix, pdata_re, cannot be changed and we cannot somehow "skip" points from it. So we will leave the standard output as is, and will simply supplement it with our own output, which we will create with the plot() function available in the Variables section.

We use getind(data_label>=0) to get all element indices of the data_label vector, which consists of the observation times for each point. This works because the condition data_label>=0 is True for all points. The next line (see below) filters out every 8th point with the help of the modulo function.

Note that there are two plots on the output: one poroduced by our plot() call, and one that visualizes the standard simulation output.

In [66]:
spinev('melo -oes --markers=.',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]32)x2
power(kHz)         [0]32          
phase(deg)         [0]32        
freq_offs(kHz)     [0]32       
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(8*spinning_freq)
power_1_1=N*spinning_freq
R=["0 180 90 270 270 90 180 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,8)==1)
plot(data_label(ind),pdata_re(ind,:),"--markers=.")
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        35 50 75
n_gamma            *
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes --markers=. -px -py0 -vm -v0 --verb --prev
options_cmd_line   -oes --markers=. -px -py0 -vm -v0 --verb --prev
+ *************************
+ all=getind(data_label>=0)
+ ind=getind(mod(all,8)==1)
+ plot(data_label(ind),pdata_re(ind,:),"--markers=.")

2022-03-15T04:51:13.121706 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:51:13.237809 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Now restore the powder averaging and the full evolution length:

In [67]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]32)x10
power(kHz)         [0]32          
phase(deg)         [0]32        
freq_offs(kHz)     [0]32       
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(8*spinning_freq)
power_1_1=N*spinning_freq
R=["0 180 90 270 270 90 180 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,8)==1)
plot(data_label(ind),pdata_re(ind,:))
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- timing(usec)      ([0]32)x2
+ timing(usec)      ([0]32)x10
- plot(data_label(ind),pdata_re(ind,:),"--markers=.")
+ plot(data_label(ind),pdata_re(ind,:))
- EulerAngles        35 50 75
+ EulerAngles        rep376
- n_gamma            *
+ n_gamma            15

2022-03-15T04:51:55.027217 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:51:55.146849 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

And let's see if we get a smoother curve if we sample twice per rotor cycle:

In [68]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]32)x10
power(kHz)         [0]32          
phase(deg)         [0]32        
freq_offs(kHz)     [0]32       
******* Variables **********************************
spinning_freq=7
scan_par N/4 5/
pulse_1_1=1000/(8*spinning_freq)
power_1_1=N*spinning_freq
R=["0 180 90 270 270 90 180 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,4)==1)
plot(data_label(ind),pdata_re(ind,:))
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- ind=getind(mod(all,8)==1)
+ ind=getind(mod(all,4)==1)

2022-03-15T04:52:41.569457 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:52:41.681608 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Looks perfect now.

So let's get the curves for other N:

In [69]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]32)x10
power(kHz)         [0]32          
phase(deg)         [0]32        
freq_offs(kHz)     [0]32       
******* Variables **********************************
spinning_freq=7
scan_par N/1:5/
pulse_1_1=1000/(8*spinning_freq)
power_1_1=N*spinning_freq
R=["0 180 90 270 270 90 180 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,4)==1)
plot(data_label(ind),pdata_re(ind,:))
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- scan_par N/4 5/
+ scan_par N/1:5/

2022-03-15T04:53:40.444278 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:53:40.626876 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
In [70]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]32)x10
power(kHz)         [0]32          
phase(deg)         [0]32        
freq_offs(kHz)     [0]32       
******* Variables **********************************
spinning_freq=7
scan_par N/6:10/
pulse_1_1=1000/(8*spinning_freq)
power_1_1=N*spinning_freq
R=["0 180 90 270 270 90 180 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,4)==1)
plot(data_label(ind),pdata_re(ind,:))
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- scan_par N/1:5/
+ scan_par N/6:10/

2022-03-15T04:54:20.969416 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:54:21.093217 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Now let's go back to the basic MELODRAMA sequence and compute the dephasing for odd and even N separately:

In [73]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]16)x10
power(kHz)         [0]16          
phase(deg)         [0]16        
freq_offs(kHz)     [0]16       
******* Variables **********************************
spinning_freq=7
scan_par N/1:2:9/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
R=["0 90 90 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,8)==1)
plot(data_label(ind),pdata_re(ind,:))
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- timing(usec)      ([0]32)x10
+ timing(usec)      ([0]16)x10
- power(kHz)         [0]32          
- phase(deg)         [0]32        
- freq_offs(kHz)     [0]32       
+ power(kHz)         [0]16          
+ phase(deg)         [0]16        
+ freq_offs(kHz)     [0]16       
- scan_par N/6:10/
+ scan_par N/1:2:9/
- pulse_1_1=1000/(8*spinning_freq)
+ pulse_1_1=1000/(4*spinning_freq)
- R=["0 180 90 270 270 90 180 0"]'
+ R=["0 90 90 0"]'
- ind=getind(mod(all,4)==1)
+ ind=getind(mod(all,8)==1)

2022-03-15T04:57:05.275010 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:57:05.395238 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
In [74]:
spinev('melo -oes',opts)
****** The System *******
spectrometer(MHz)  315
spinning_freq(kHz) 10.0
channels           C13
nuclei             C13 C13
atomic_coords      1.481
cs_isotropic       *
csa_parameters     *
csa_parameters     *
j_coupling         *
quadrupole         *
dip_switchboard    *
csa_switchboard    *
exchange_nuclei    *
bond_len_nuclei    *
bond_ang_nuclei    *
tors_ang_nuclei    *
groups_nuclei      *
*** melo = R1 R2 R2 R1
CHN 1
timing(usec)      ([0]16)x10
power(kHz)         [0]16          
phase(deg)         [0]16        
freq_offs(kHz)     [0]16       
******* Variables **********************************
spinning_freq=7
scan_par N/2:2:10/
pulse_1_1=1000/(4*spinning_freq)
power_1_1=N*spinning_freq
R=["0 90 90 0"]'
B=mod(R+180,360)   // 'R-bar'
phase_1_1 = R; B; B; R
*************************
all=getind(data_label>=0)
ind=getind(mod(all,4)==1)
plot(data_label(ind),pdata_re(ind,:))
*********************************************
y_label = "${\langle}I_{1z}+I_{2z}{\rangle}$"
******* Options *************************************
rho0               F1z
observables        0.5*F1z
EulerAngles        rep376
n_gamma            15
line_broaden(Hz)   *
zerofill           *
FFT_dimensions     *
options            -re -py
options_cmd_line   -oes -px -py0 -vm -v0 --verb --prev
- scan_par N/1:2:9/
+ scan_par N/2:2:10/
- ind=getind(mod(all,8)==1)
+ ind=getind(mod(all,4)==1)

2022-03-15T04:57:52.561523 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/
2022-03-15T04:57:52.693848 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/

Conclusion

We can see that the basic (4-step) MELODRAMA sequence has three cases for the recoupled Hamiltonian it generates: N=1, odd N, and even N.

The cyclic (8-step) version of MELODRAMA recouples more uniformly for different N, except for N=1. The strongest recoupling is observed for the N=3 case.

The analytic (first-order AHT) estimate of the recoupled Hamiltonian is off by at least a factor of 2 for the N=1 case with both versions of the sequence.

It appears that the authors did not realize that the basic version of their pulse sequence has very different average Hamiltonians for odd and even N. The 8-step version is also somewhat sensitive to the N value, but to a much lesser degree. Thus, experimantally, it may appear that it just compensates for RF inhomogeneity, while in reality it produces a very different recoupling.