Pulse Sequencer

PyEPR provides an intuitive object-oriented pulse programmer allowing the user to design pulsesequences in a hardware-agnostic manner. Additionally, several common EPR experiments are pre-defined and can be easily instantiated and modified.

PyEPR uses ns, GHz and G as the default time, frequency and field units. Very occasionally, other units such as µs or MHz are used, in which case it will be explicitly mentioned.

Seqeunce Construction

First we must import the pyepr package, and set our waveform precision. This waveform precision determines the time resolution of the pulse length and positons. If values are given that are not multiples of the waveform precision, they will be rounded.

import pyepr as epr
epr.set_waveform_precision(2)  # Set waveform precision to 2 ns

Next, we want to create a sequence object. Here we are aiming to create a simple Hahn Echo sequence. It is normally recommened, to set the field B and frequency freq as external variables.

B = 12200  # Magnetic field in Gauss
freq = 34.0  # Microwave frequency in GHz
seq = epr.Sequence(name='Hahn Echo Sequence',
                   B = B,
                   freq = freq,
                   reptime = 3e3,# Repetition time in us
                   averages = 1, 
                   shots = 20, # Number of shots per point
                   ) 

Now we need to define some pulses that can be used in our sequence. Here we create a 90 degree and a 180 degree rectangular pulse. These pulses will eventually need a scale (amplitude), before the sequence can be run on hardware. A Detection window is also created

p90 = epr.RectPulse(tp=16,
                    freq=0, # Frequency offset in MHz, w.r.t the sequence frequency,
                    flipangle=np.pi/2, # Flip angle in degrees
                    pcyc = {"phases":[0, np.pi], "dets":[1,-1]}
                    )
p180 = epr.RectPulse(tp=32,
                    freq=0, # Frequency offset in MHz, w.r.t the sequence frequency,
                    flipangle=np.pi, # Flip angle in degrees
                    )
det = epr.Detetction(tp=32,
                    freq=0, # Frequency offset in MHz, w.r.t the sequence frequency,
                    )

We now need a time axis for our sequence and to add them to the sequence object. When a pulse is copied into the sequence using the add_pulse method, parameters can be modified allowing the same pulse can be used multiple times with different timings or amplitudes.

t = epr.Parameter(name='Interpulse Delay',
                  value=400, # Initial interpulse delay in ns
                  step=8,  # Step size in ns
                  dim=1024  # Number of points,
                  unit='ns'  # Unit of the parameter
                  description='Interpulse delay between the pi/2 and pi pulse'
                  )

# Adding the pulses to the sequence
seq.add_pulse(p90.copy(t=0))
seq.add_pulse(p180.copy(t=t))
seq.add_pulse(det.copy(t=2*t))  

# Defining the evolution
seq.evolution([t])

Advanced Sequences

The sequence class is capable of more advanced features such as:

  • Linked axes

  • Reduced axes

  • Non-linear axes

Default Experiments

For convienience, several common EPR experiments are pre-defined and can be easily instantiated and modified. A list of currently implemented experiments can be found in the API documentation. Here we show how to create a simple Hahn Echo Relaxation experiment.

HE_Seq = epr.HahnEchoRelaxationSequence(
    B = B,
    freq = freq,
    reptime = 3e3, # Repetition time in us
    averages = 1,
    shots = 20, # Number of shots per point
    start = 400, # Initial interpulse delay in ns
    step = 8,  # Step size in ns
    dim = 1024  # Number of points
    pi2_pulse = p90,  # The 90 degree pulse
    pi_pulse = p180  # The 180 degree pulse
)

Advanced Pulses

More complex pulse shapes can be created using the pulse classes provided in the pyepr.pulses module. A list of currently implemented pulse shapes, and their necessary inputs can be found in the API documentation.

Here we show how to create a simple chirp pulse

from pyepr.pulses import ChirpPulse

pulse = ChirpPulse(
    tp = 128,  # Pulse length in ns
    init_freq = -0.1,  # Frequency offset in GHz, w.r.t the sequence frequency,
    final_freq = 0.1,  # Frequency offset in GHz, w.r.t the sequence frequency,
    flipangle = np.pi,  # Flip angle in radians
)