Skip to content

Commit

Permalink
Merge branch '2D_I_GUI' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
HKaras authored May 6, 2024
2 parents 98c99e6 + d979d9a commit 8c6817f
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 255 deletions.
130 changes: 129 additions & 1 deletion autodeer/DEER_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import scipy.signal as sig
from scipy.optimize import minimize,brute
from autodeer.colors import primary_colors, ReIm_colors
from autodeer.utils import round_step


log = logging.getLogger('autoDEER.DEER')
Expand Down Expand Up @@ -324,6 +325,21 @@ def background_func(t, fit):
return scale * prod

def calc_correction_factor(fit_result,aim_MNR=25,aim_time=2):
"""
Calculate the correction factor for the number of averages required to achieve a given MNR in a given time.
Parameters
----------
fit_result : Deerlab.FitResult
The fit result from the DEER analysis.
aim_MNR : float, optional
The desired MNR, by default 25
aim_time : float, optional
The desired time in hours, by default 2
Returns
-------
float
The correction factor for the number of averages.
"""

dataset = fit_result.dataset
runtime_s = dataset.nAvgs * dataset.nPcyc * dataset.shots * dataset.reptime * dataset.t.shape[0] * 1e-6
Expand Down Expand Up @@ -944,4 +960,116 @@ def plot_overlap(Fieldsweep, pump_pulse, exc_pulse, ref_pulse, filter=None, resp
axs.legend()
axs.set_xlabel('Frequency (MHz)')

return fig
return fig

def calc_deer_settings(experiment:str, CPdecay=None, Refocused2D=None, target_time=2,target_MNR=20, waveform_precision=2):
"""
Calculates the optimal DEER settings based on the avaliable relaxation data
Parameters
----------
experiment : str
Type of DEER experiment, either 'auto', '4pDEER' or '5pDEER'
CPdecay : ad.CarrPurcellAnalysis
Carr-Purcell relaxation data
Refocused2D : ad.RefocusedEcho2DAnalysis, optional
Refocused 2D data required for '4pDEER', by default None
target_time : int, optional
Target time for the DEER experiment in hours, by default 2
target_MNR : float, optional
Target modulation to noise ratio, by default 20
waveform_precision : int, optional
Precision of the waveform in ns, by default 2
Returns
-------
dict
DEER settings, with keys:
-'ExpType': '4pDEER' or '5pDEER'
-'tau1': in us
-'tau2': in us
-'tau3': in us, only for 5pDEER
-'AimTime': in hours
Notes
-----
This function will calcate the optimal DEER settings based on the avaliable relaxation data, depending on the experiment type.
For 4pDEER, the optimal tau1 and tau2 are calculated based on the refocused 2D data, and for 5pDEER, the optimal tau2 is calculated based on the CPdecay data or refocused 2D if CP decay data is not availiable.
If the optimal tau2 for 5pDEER is less than 1.5us, the function will calculate the optimal tau1 and tau2 for 4pDEER instead. This is only possible if the refocused 2D data is availiable, otherwise a non optimal tau1 of 0.4us is used.
"""

# Calculate the DEER settings from the avaliable relaxation data
# Move out of the GUI Code asap

deer_settings = {}

if experiment == '4pDEER':
if Refocused2D is None:
raise ValueError("Refocused2D data required for 4pDEER")
# Calcualting a 4pDEER Sequence
deer_settings['ExpType'] = '4pDEER'

# Calculate tau1 and tau2
tau1,tau2 = Refocused2D.find_optimal(type='4pDEER',SNR_target=20, target_time=target_time, target_step=0.015)

if tau2 < 2.5:
# 2hr dipolar evo too short for meaningful results. Using double the time instead
target_time *= 2
tau1,tau2 = Refocused2D.find_optimal(type='4pDEER',SNR_target=20, target_time=target_time, target_step=0.015)

if tau2 < 2.5:
# Dipolar evo time still too short. Hardcoding a 2.5us dipolar evo time
tau1,tau2 = Refocused2D.optimal_tau1(type='4pDEER',tau2=2.5)


deer_settings['tau1'] = round_step(tau1,waveform_precision/1e3)
deer_settings['tau2'] = round_step(tau2,waveform_precision/1e3)
deer_settings['AimTime'] = target_time

elif (experiment == '5pDEER') or (experiment == 'auto'):
# Calculate a 5pDEER Sequence
if CPdecay is not None:
tau2hrs = CPdecay.find_optimal(SNR_target=target_MNR, target_time=target_time, target_step=0.015)
tau4hrs = CPdecay.find_optimal(SNR_target=target_MNR, target_time=target_time*2, target_step=0.015)
elif Refocused2D is not None:
tau2hrs = Refocused2D.find_optimal(type='5pDEER',SNR_target=20, target_time=target_time, target_step=0.015)
tau4hrs = Refocused2D.find_optimal(type='5pDEER',SNR_target=20, target_time=target_time*2, target_step=0.015)
else:
raise ValueError("CPdecay data required for 5pDEER")


if (tau2hrs < 1.5) and (tau4hrs > 1.5):
# 2hr dipolar evo too short for meaningful results. Using 4hr number instead
deer_settings['tau1'] = round_step(tau4hrs,waveform_precision/1e3)
deer_settings['tau2'] = round_step(tau4hrs,waveform_precision/1e3)
deer_settings['tau3'] = round_step(0.3,waveform_precision/1e3)
deer_settings['ExpType'] = '5pDEER'
deer_settings['AimTime'] = target_time*2

elif (tau2hrs < 1.5) and (tau4hrs < 1.5):
# 2hr & 4hr dipolar evo too short for meaningful results. Hardcoding a 2.5us dipolar evo time, in 4pDEER and 4hrs
# self.raise_warning(f"2hr dipolar evo too short. Hardcoding a 2.5us dipolar evo time")
tau2 = 2.5
if Refocused2D is not None:
tau1 = Refocused2D.optimal_tau1(type='4pDEER',tau2=tau2)
else:
tau1 = 0.4

deer_settings['tau1'] = round_step(tau1,waveform_precision/1e3)
deer_settings['tau2'] = round_step(tau2,waveform_precision/1e3)

deer_settings['ExpType'] = '4pDEER'
deer_settings['AimTime'] = target_time * 2
# self.raise_warning(f"2hr dipolar evo {tau2hrs:.2f} us, using 4pDEER")

else:
# Normal case, using 2hr dipolar evo time
deer_settings['tau1'] = round_step(tau2hrs,waveform_precision/1e3)
deer_settings['tau2'] = round_step(tau2hrs,waveform_precision/1e3)
deer_settings['tau3'] = round_step(0.3,waveform_precision/1e3)
deer_settings['ExpType'] = '5pDEER'
deer_settings['AimTime'] = target_time

return deer_settings
76 changes: 76 additions & 0 deletions autodeer/Relaxation.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,5 +439,81 @@ def plot1D(self,axs=None,fig=None):
axs.set_ylabel('Signal / A.U.')

return fig

def find_optimal(self,type:str,SNR_target, target_time: float, target_step, averages=None) -> float:
"""Calculate the optimal inter pulse delay for a given total measurment time, using either 4pulse or 5pulse data.
Parameters
----------
type : str
The type of data to use, either '4pDEER' or '5pDEER'
SNR_target : float
The Signal to Noise ratio target.
target_time : float
The target time in hours
target_step: float
The target step size in ns.
averages : int, optional
The total number of shots taken, by default None. If None, the
number of shots will be calculated from the dataset.
Returns
-------
tau1: float
The calculated optimal tau1 in us
tau2: float
The calculated optimal tau2 in us
Notes:
------
The shot repetition time is assumed to be the same as for the relxaation data and is taken from the dataset.
"""

dataset = self.dataset
if averages is None:
averages = dataset.nAvgs * dataset.shots * dataset.nPcyc
target_shrt = dataset.reptime * 1e-6

data = np.abs(self.data)
data /= np.max(data)

calc_data = data

# averages = self.seq.shots.value * self.seq.averages.value
self.noise = noiselevel(data)
data_snr = calc_data / self.noise
data_snr_avgs = data_snr / np.sqrt(averages)

if type == '4pDEER':
data_snr_avgs_tau2 = np.max(data_snr_avgs,axis=1)

# Target time
target_time = target_time * 3600
g = (target_time * target_step / target_shrt) * 1/(self.axis[1].data)
f = (SNR_target/data_snr_avgs_tau2)**2

tau2_idx = np.argmin(np.abs(g-f))
self.tau2 = self.axis[1].data[tau2_idx]
self.tau1 = self.axis[0].data[np.argmax(data_snr_avgs[:,tau2_idx])]
return self.tau1, self.tau2

elif type == '5pDEER':
data_snr_avgs_CP = np.diag(data_snr_avgs)
target_time = target_time * 3600
g = (target_time * target_step / target_shrt) * 1/(self.axis[1].data)
f = (SNR_target/data_snr_avgs_CP)**2
tau2_idx = np.argmin(np.abs(g-f))
self.tau2 = self.axis[1].data[tau2_idx]
return self.tau2, self.tau2



def optimal_tau1(self,tau2=None,):

tau2_idx = np.argmin(np.abs(self.axis[1].data - tau2))
self.tau1 = self.axis[0].data[np.argmax(self.data[:,tau2_idx])]
return self.tau1




2 changes: 1 addition & 1 deletion autodeer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .utils import *
from .FieldSweep import FieldSweepAnalysis
from .ResPro import ResonatorProfileAnalysis, optimise_spectra_position
from .DEER_analysis import DEERanalysis,DEERanalysis_plot, DEERanalysis_plot_pub,IdentifyROI,optimise_pulses,plot_overlap,normalise_01
from .DEER_analysis import DEERanalysis,DEERanalysis_plot, DEERanalysis_plot_pub,IdentifyROI,optimise_pulses,plot_overlap,normalise_01, calc_correction_factor, calc_deer_settings
from .Relaxation import *
from .classes import Parameter, Interface
from .sequences import *
Expand Down
Loading

0 comments on commit 8c6817f

Please sign in to comment.