From d2094a7965d05ea8274af4253c29a4dc581ae65b Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Thu, 2 May 2024 11:02:04 +0200 Subject: [PATCH 1/7] Adding a 2D dataset to the the dummy --- autodeer/hardware/dummy.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/autodeer/hardware/dummy.py b/autodeer/hardware/dummy.py index 47b54f5c..20ea7b56 100644 --- a/autodeer/hardware/dummy.py +++ b/autodeer/hardware/dummy.py @@ -111,6 +111,8 @@ def acquire_dataset(self,**kwargs): axes, data = _simulate_reptimescan(self.cur_exp) elif isinstance(self.cur_exp,T2RelaxationSequence): axes, data = _simulate_T2(self.cur_exp,self.ESEEM) + elif isinstance(self.cur_exp,RefocusedEcho2DSequence): + axes, data = _simulate_2D_relax(self.cur_exp) else: raise NotImplementedError("Sequence not implemented") @@ -276,9 +278,18 @@ def func(x,T1): data = add_phaseshift(data, 0.05) return t, data -# def _simulate_2D_relax(sequence): +def _simulate_2D_relax(sequence): + sigma = 0.8 + func = lambda x, y: np.exp(-((x**2 + y**2 - 1*x*y) / (2*sigma**2))) + + xaxis = val_in_us(sequence.tau1) + yaxis = val_in_us(sequence.tau2) + + X, Y = np.meshgrid(xaxis, yaxis) + data = func(X, Y) + data = add_phaseshift(data, 0.05) + return [xaxis, yaxis], data -# def func(x,y,T2): def _gen_ESEEM(t,freq,depth): From 071ab1e1321251636ce685a38c47e581ee14f9ca Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Fri, 3 May 2024 09:48:45 +0200 Subject: [PATCH 2/7] Reformulated Refocused2D --- autodeer/gui/autoDEER_worker.py | 90 ++++++++++++++++++--------------- autodeer/sequences.py | 15 +++--- docsrc/source/releasenotes.rst | 4 ++ 3 files changed, 63 insertions(+), 46 deletions(-) diff --git a/autodeer/gui/autoDEER_worker.py b/autodeer/gui/autoDEER_worker.py index 84133ef2..98abf4d5 100644 --- a/autodeer/gui/autoDEER_worker.py +++ b/autodeer/gui/autoDEER_worker.py @@ -34,6 +34,7 @@ class autoDEERSignals(QtCore.QObject): respro_result = QtCore.pyqtSignal(object) # optimise_pulses = QtCore.pyqtSignal(object) relax_result = QtCore.pyqtSignal(object) + Relax2D_result = QtCore.pyqtSignal(object) T2_result = QtCore.pyqtSignal(object) quickdeer_result = QtCore.pyqtSignal(object) longdeer_result = QtCore.pyqtSignal(object) @@ -79,6 +80,8 @@ def __init__(self, interface, wait:QtCore.QWaitCondition, mutex:QtCore.QMutex,pu self.stop_flag = False self.quick_deer_state = True + self.max_tau = 5 + if (self.project is None) or (self.project == ''): def savename(exp, suffix=""): if suffix != "": @@ -227,48 +230,22 @@ def run_T2_relax(self,dt=60): def run_2D_relax(self): - self.signals.status.emit('Running 2D relaxation experiment') + self.signals.status.emit('Running 2D decoherence experiment') LO = self.LO gyro = self.gyro reptime = self.reptime - # def run_quick_deer(self, dt=16): - # if ('tau1' in self.user_inputs) and (self.user_inputs['tau1'] != 0): - # self.signals.status.emit('Skipping QuickDEER') - # return 'skip' - # else: - # self.signals.status.emit('Running QuickDEER') - - # tau = self.results['relax'].tau2hrs - - # LO = self.LO - # gyro = self.gyro - # reptime = self.reptime - # deer = DEERSequence( - # B=LO/gyro, LO=LO,reptime=reptime,averages=150,shots=int(15*self.noise_mode), - # tau1=tau, tau2=tau, tau3=0.3, dt=dt, - # exc_pulse=self.pulses['exc_pulse'], ref_pulse=self.pulses['ref_pulse'], - # pump_pulse=self.pulses['pump_pulse'], det_event=self.pulses['det_event'] - # ) - - # deer.five_pulse() - # if self.AWG: - # deer.select_pcyc("16step_5p") - # else: - # deer.select_pcyc("DC") - # deer._estimate_time(); - - # # build criteria - - - # self.interface.launch(deer,savename=f"{self.samplename}_quickdeer",IFgain=2) - # with threadpool_limits(limits=self.cores, user_api='blas'): - # self.interface.terminate_at(total_crit,verbosity=2,test_interval=0.5) - # while self.interface.isrunning(): - # time.sleep(self.updaterate) + seq = RefocusedEcho2DSequence( + B=LO/gyro, LO=LO,reptime=reptime,averages=10,shots=int(20*self.noise_mode), + tau=self.max_tau, pi2_pulse=self.pulses['exc_pulse'], + pi_pulse=self.pulses['ref_pulse'], det_event=self.pulses['det_event']) - # self.signals.quickdeer_result.emit(self.interface.acquire_dataset()) - # self.signals.status.emit('QuickDEER experiment complete') + self.interface.launch(seq,savename=self.savename("2D_DEC"),IFgain=2) + self.interface.terminate_at(SNRCriteria(10),test_interval=0.5) + while self.interface.isrunning(): + time.sleep(self.updaterate) + self.signals.Relax2D_result.emit(self.interface.acquire_dataset()) + self.signals.status.emit('2D decoherence experiment complete') def run_quick_deer(self): @@ -390,6 +367,33 @@ def tune_pulses(self): return 'skip' + def _build_methods(self): + + seq = self.deer_inputs['ExpType'] + + if (self.deer_inputs['tau1'] == 0) and (self.deer_inputs['tau2'] == 0): + quick_deer = True + else: + quick_deer = False + + methods = [self.run_fsweep,self.run_reptime_opt,self.run_respro,self.run_fsweep, + self.tune_pulses] + + if (seq is None) or (seq == '5pDEER'): + methods.append(self.run_CP_relax) + elif seq == '4pDEER': + methods.append(self.run_CP_relax) + methods.append(self.run_2D_relax) + + methods.append(self.run_T2_relax) + + if quick_deer: + methods.append(self.run_quick_deer) + + methods.append(self.run_long_deer) + + return methods + @QtCore.pyqtSlot() def run(self): @@ -397,9 +401,11 @@ def run(self): self.stop_flag = False - methods = [self.run_fsweep,self.run_reptime_opt,self.run_respro,self.run_fsweep, - self.tune_pulses,self.run_CP_relax,self.run_T2_relax,self.run_quick_deer, - self.run_long_deer] + # methods = [self.run_fsweep,self.run_reptime_opt,self.run_respro,self.run_fsweep, + # self.tune_pulses,self.run_CP_relax,self.run_T2_relax,self.run_quick_deer, + # self.run_long_deer] + + methods = self._build_methods() for method in methods: if self.stop_flag: @@ -432,6 +438,10 @@ def update_reptime(self,reptime): def update_gyro(self,gyro): self.gyro = gyro + + def set_2D_max_tau(self, max_tau): + self.max_tau = max_tau + def update_deersettings(self,deer_settings): self.deer_inputs = deer_settings diff --git a/autodeer/sequences.py b/autodeer/sequences.py index 39e90e59..0461cda6 100644 --- a/autodeer/sequences.py +++ b/autodeer/sequences.py @@ -1624,7 +1624,7 @@ class RefocusedEcho2DSequence(Sequence): Represents a 2D Refocused-echo Sequence. """ def __init__(self, *, B, LO, reptime, averages, shots, - tau, dim=100, step=50, **kwargs) -> None: + tau, dim=100, **kwargs) -> None: """Build a 2D Refocused-echo sequence using either rectangular pulses or specified pulses. @@ -1641,11 +1641,10 @@ def __init__(self, *, B, LO, reptime, averages, shots, shots : int The number of shots per point tau : int - The starting value in ns + The maximum total sequence length in us dim: int The number of points in both the X and Y axis - step: float - The step in ns for both the X and Y axis + Optional Parameters ------------------- @@ -1661,9 +1660,13 @@ def __init__(self, *, B, LO, reptime, averages, shots, super().__init__( name=name, B=B, LO=LO, reptime=reptime, averages=averages, shots=shots, **kwargs) - self.tau1 = Parameter(name="tau1", value=tau, dim=dim, step=step, unit="ns", + + tau_init = 400 + dt = (tau * 1e3) / dim + + self.tau1 = Parameter(name="tau1", value=tau_init, dim=dim, step=dt, unit="ns", description="1st interpulse delay") - self.tau2 = Parameter(name="tau2", value=tau, dim=dim, step=step, unit="ns", + self.tau2 = Parameter(name="tau2", value=tau_init, dim=dim, step=dt, unit="ns", description="2nd interpulse delay") if "pi_pulse" in kwargs: diff --git a/docsrc/source/releasenotes.rst b/docsrc/source/releasenotes.rst index b844b61c..fd9e3d03 100644 --- a/docsrc/source/releasenotes.rst +++ b/docsrc/source/releasenotes.rst @@ -1,6 +1,10 @@ Release Notes ============= +Version 0.8.0 (TBA): ++++++++++++++++++++++++++++ +- Major Support Update for 2D Decoherence + - `RefocusedEcho2DSequence` reforumlated to support match normal style Version 0.7.0 (2024-04-01): +++++++++++++++++++++++++++ From b8d6404c3eccd7cea33cc07b0611da907f10205e Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Fri, 3 May 2024 16:58:41 +0200 Subject: [PATCH 3/7] Added Optimisation function to Refocused2D --- autodeer/Relaxation.py | 76 +++++++++++++++++++++++++++++++++++++++++ test/test_Bruker_AWG.py | 6 ---- test/test_Relaxation.py | 36 +++++++++++++++++-- test/test_xepr.py | 61 --------------------------------- 4 files changed, 109 insertions(+), 70 deletions(-) delete mode 100644 test/test_Bruker_AWG.py delete mode 100644 test/test_xepr.py diff --git a/autodeer/Relaxation.py b/autodeer/Relaxation.py index d85781ca..9d10d078 100644 --- a/autodeer/Relaxation.py +++ b/autodeer/Relaxation.py @@ -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 + + \ No newline at end of file diff --git a/test/test_Bruker_AWG.py b/test/test_Bruker_AWG.py deleted file mode 100644 index 3be207d8..00000000 --- a/test/test_Bruker_AWG.py +++ /dev/null @@ -1,6 +0,0 @@ -from autodeer.hardware import BrukerAWG - -config_file = "test/test_data/test_Bruker_config.yaml" - -def test_create_interface(): - interface = BrukerAWG(config_file) diff --git a/test/test_Relaxation.py b/test/test_Relaxation.py index 4eaa167a..a2f382e8 100644 --- a/test/test_Relaxation.py +++ b/test/test_Relaxation.py @@ -1,7 +1,7 @@ from autodeer.Relaxation import * from autodeer import eprload -from autodeer.hardware.dummy import _simulate_CP, _simulate_reptimescan -from autodeer.sequences import DEERSequence, ReptimeScan +from autodeer.hardware.dummy import _simulate_CP, _simulate_reptimescan, _simulate_2D_relax +from autodeer.sequences import DEERSequence, ReptimeScan, RefocusedEcho2DSequence from autodeer.dataset import * import pytest from matplotlib.figure import Figure as mplFigure @@ -48,4 +48,34 @@ def test_ReptimeAnalysis_from_dataset(): assert np.abs(optimal - 3e3)/3e3 < 0.1 fig = RA.plot() - assert isinstance(fig,mplFigure) \ No newline at end of file + assert isinstance(fig,mplFigure) + +# --------------------------- Test RefocusedEcho2DAnalysis ------------------------ + +def get_Ref2DAnalysis(): + seq = RefocusedEcho2DSequence( + B=12220, LO=34.0, reptime=3e3,averages=1, shots=50, tau=10,) + x,V = _simulate_2D_relax(seq) + dataset = create_dataset_from_sequence(V,seq) + dataset.attrs['nAvgs'] = 1 + Ref2D = RefocusedEcho2DAnalysis(dataset) + return Ref2D + +def test_RefocusedEcho2DAnalysis_from_dataset(): + Ref2D = get_Ref2DAnalysis() + + tau1, tau2 = Ref2D.find_optimal(type='4pDEER', SNR_target=20, target_time=2, target_step=15) + assert tau1 == pytest.approx(4.0,rel=1e-3) + assert tau2 == pytest.approx(7.9,rel=1e-3) + + tau1_2 = Ref2D.optimal_tau1(tau2) + assert tau1_2 == pytest.approx(tau1,rel=1e-3) + + tau1, tau2 = Ref2D.find_optimal(type='5pDEER', SNR_target=20, target_time=2, target_step=15) + assert tau1 == pytest.approx(6.9,rel=1e-3) + assert tau2 == pytest.approx(6.9,rel=1e-3) + + + + + diff --git a/test/test_xepr.py b/test/test_xepr.py deleted file mode 100644 index 8314f8dc..00000000 --- a/test/test_xepr.py +++ /dev/null @@ -1,61 +0,0 @@ -# Here is a collection of pytests for the Xepr module. -# This is very limited due to the obvious lack of a spectrometer. - -import autodeer.hardware.xepr_experiments as x_exp -import re -from autodeer.hardware.XeprAPI_link import XeprAPILink -import pytest - -def test_change_dimensions(): - path = "autoDeer/PulseSpel/HUKA_DEER_AWG.exp" - dim = 8 - new_dim = 256 - x_exp.change_dimensions(path, dim, new_dim) - - with open(path, 'r') as file: - data = file.readlines() - - re_search = fr"dim{int(dim)}\s*s\[([0-9]+),*[0-9]*\]" - - for line in data: - output = re.findall(re_search, line) - if output != []: - match = int(output[0]) - - assert match == new_dim - - -def test_create_from_config(): - xepr = XeprAPILink("test/test_data/test_Bruker_config.yaml") - - assert xepr.AWG is False - - -def test_min_freq(): - xepr = XeprAPILink("test/test_data/test_Bruker_config.yaml") - with pytest.raises(RuntimeError): - xepr.set_freq(30) - - -def test_max_freq(): - xepr = XeprAPILink("test/test_data/test_Bruker_config.yaml") - with pytest.raises(RuntimeError): - xepr.set_freq(40) - - -def test_eldor_min_freq(): - xepr = XeprAPILink("test/test_data/test_Bruker_config.yaml") - with pytest.raises(RuntimeError): - xepr.set_ELDOR_freq(30) - - -def test_eldor_max_freq(): - xepr = XeprAPILink("test/test_data/test_Bruker_config.yaml") - with pytest.raises(RuntimeError): - xepr.set_ELDOR_freq(40) - - -def test_bridge_dt(): - xepr = XeprAPILink("test/test_data/test_Bruker_config.yaml") - with pytest.raises(ValueError): - xepr.set_PulseSpel_var("p1", 3) From d68e4bbdfc59d935ed29e6f71fac11ee613ac68f Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Fri, 3 May 2024 17:17:33 +0200 Subject: [PATCH 4/7] Added the option to only run Refocused2D and not DEER --- autodeer/gui/autoDEER_worker.py | 5 ++++- autodeer/gui/main.py | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/autodeer/gui/autoDEER_worker.py b/autodeer/gui/autoDEER_worker.py index 98abf4d5..7245f60b 100644 --- a/autodeer/gui/autoDEER_worker.py +++ b/autodeer/gui/autoDEER_worker.py @@ -381,12 +381,15 @@ def _build_methods(self): if (seq is None) or (seq == '5pDEER'): methods.append(self.run_CP_relax) - elif seq == '4pDEER': + elif (seq == '4pDEER') or (seq == 'Ref2D'): methods.append(self.run_CP_relax) methods.append(self.run_2D_relax) methods.append(self.run_T2_relax) + if seq == 'Ref2D': + return methods + if quick_deer: methods.append(self.run_quick_deer) diff --git a/autodeer/gui/main.py b/autodeer/gui/main.py index 16ea4f56..d4f4e9b8 100644 --- a/autodeer/gui/main.py +++ b/autodeer/gui/main.py @@ -176,7 +176,7 @@ def __init__(self): self.queue = Queue() self.FullyAutoButton.clicked.connect(self.RunFullyAutoDEER) - self.AdvancedAutoButton.clicked.connect(self.RunAdvanedAutoDEER) + self.AdvancedAutoButton.clicked.connect(self.RunAdvancedAutoDEER) docs_url = QtCore.QUrl('https://jeschkelab.github.io/autoDEER/') github_url = QtCore.QUrl('https://github.com/JeschkeLab/autoDEER/') @@ -301,7 +301,7 @@ def load_spectrometer_config(self, filename=None): if model == 'Dummy': from autodeer.hardware.dummy import dummyInterface self.spectromterInterface = dummyInterface(filename_edit) - self.spectromterInterface.savefolder = self.current_folder + self.spectromterInterface._savefolder = self.current_folder self.Bruker=False elif model == 'ETH_AWG': from autodeer.hardware.ETH_awg import ETH_awg_interface @@ -858,9 +858,9 @@ def refresh_T2(self, fitresult): def advanced_mode_inputs(self): - self.Exp_types.addItems(['5pDEER','4pDEER','3pDEER','nDEER']) - self.ExcPulseSelect.addItems(['Auto', 'Rectangular','Chirp','HS', 'Gauss']) - self.RefPulseSelect.addItems(['Auto', 'Rectangular','Chirp','HS', 'Gauss']) + self.Exp_types.addItems(['5pDEER','4pDEER','Ref2D']) + self.ExcPulseSelect.addItems(['Auto', 'Rectangular','Gauss']) + self.RefPulseSelect.addItems(['Auto', 'Rectangular', 'Gauss']) self.PumpPulseSelect.addItems(['Auto', 'Rectangular','Chirp','HS', 'Gauss']) def update_quickdeer(self, dataset=None): From df9cf7386db255b4cd5d8f881525bdfbe4d93222 Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Fri, 3 May 2024 17:19:27 +0200 Subject: [PATCH 5/7] Moved the code for calc_deer_settings to DEER_analysis script --- autodeer/DEER_analysis.py | 130 ++++++++++++++++++++++++++++++++- autodeer/__init__.py | 2 +- autodeer/gui/main.py | 42 ++++++----- autodeer/hardware/dummy.py | 6 +- docsrc/source/API_docs.rst | 4 + docsrc/source/releasenotes.rst | 7 ++ test/test_DEER_analysis.py | 48 +++++++++++- 7 files changed, 213 insertions(+), 26 deletions(-) diff --git a/autodeer/DEER_analysis.py b/autodeer/DEER_analysis.py index c92173ad..e9a68d9a 100644 --- a/autodeer/DEER_analysis.py +++ b/autodeer/DEER_analysis.py @@ -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') @@ -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 @@ -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 \ No newline at end of file + 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 diff --git a/autodeer/__init__.py b/autodeer/__init__.py index 986a10b6..82483b12 100644 --- a/autodeer/__init__.py +++ b/autodeer/__init__.py @@ -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 * diff --git a/autodeer/gui/main.py b/autodeer/gui/main.py index d4f4e9b8..9a66963a 100644 --- a/autodeer/gui/main.py +++ b/autodeer/gui/main.py @@ -747,29 +747,33 @@ def refresh_relax(self, fitresult): self.current_results['relax'].tau2hrs = tau2hrs + if 'relax2D' in self.current_results: + self.deer_settings = ad.calc_deer_settings('auto',self.current_results['relax'],self.current_results['relax2D'],self.aim_time,self.aim_MNR,self.waveform_precision) + else: + self.deer_settings = ad.calc_deer_settings('auto',self.current_results['relax'],None,self.aim_time,self.aim_MNR,self.waveform_precision) - if (tau2hrs < 1.5) and (tau4hrs > 1.5): - self.raise_warning(f"2hr dipolar evo too short. Using 4hr number") - self.deer_settings['tau1'] = ad.round_step(tau4hrs,self.waveform_precision/1e3) - self.deer_settings['tau2'] = ad.round_step(tau4hrs,self.waveform_precision/1e3) - self.deer_settings['ExpType'] = '5pDEER' - self.aim_time = 4 + # if (tau2hrs < 1.5) and (tau4hrs > 1.5): + # self.raise_warning(f"2hr dipolar evo too short. Using 4hr number") + # self.deer_settings['tau1'] = ad.round_step(tau4hrs,self.waveform_precision/1e3) + # self.deer_settings['tau2'] = ad.round_step(tau4hrs,self.waveform_precision/1e3) + # self.deer_settings['ExpType'] = '5pDEER' + # self.aim_time = 4 - elif (tau2hrs < 1.5) and (tau4hrs < 1.5): - self.current_results['relax'].tau2hrs = 2.5 - self.raise_warning(f"2hr dipolar evo too short. Hardcoding a 2.5us dipolar evo time") - self.deer_settings['tau1'] = ad.round_step(0.4,self.waveform_precision/1e3) - self.deer_settings['tau2'] = ad.round_step(2.5,self.waveform_precision/1e3) - self.deer_settings['ExpType'] = '4pDEER' - self.raise_warning(f"2hr dipolar evo {tau2hrs:.2f} us, using 4pDEER") - - else: - self.deer_settings['tau1'] = ad.round_step(tau2hrs,self.waveform_precision/1e3) - self.deer_settings['tau2'] = ad.round_step(tau2hrs,self.waveform_precision/1e3) - self.deer_settings['ExpType'] = '5pDEER' + # elif (tau2hrs < 1.5) and (tau4hrs < 1.5): + # self.current_results['relax'].tau2hrs = 2.5 + # self.raise_warning(f"2hr dipolar evo too short. Hardcoding a 2.5us dipolar evo time") + # self.deer_settings['tau1'] = ad.round_step(0.4,self.waveform_precision/1e3) + # self.deer_settings['tau2'] = ad.round_step(2.5,self.waveform_precision/1e3) + # self.deer_settings['ExpType'] = '4pDEER' + # self.raise_warning(f"2hr dipolar evo {tau2hrs:.2f} us, using 4pDEER") + + # else: + # self.deer_settings['tau1'] = ad.round_step(tau2hrs,self.waveform_precision/1e3) + # self.deer_settings['tau2'] = ad.round_step(tau2hrs,self.waveform_precision/1e3) + # self.deer_settings['ExpType'] = '5pDEER' - self.deer_settings['tau3'] = 0.3 + # self.deer_settings['tau3'] = 0.3 self.deer_settings['dt'] = 16 self.worker.update_deersettings( self.deer_settings diff --git a/autodeer/hardware/dummy.py b/autodeer/hardware/dummy.py index 20ea7b56..48b3c6d1 100644 --- a/autodeer/hardware/dummy.py +++ b/autodeer/hardware/dummy.py @@ -238,9 +238,13 @@ def _simulate_deer(sequence,exp_type=None): def _simulate_CP(sequence): + if isinstance(sequence, DEERSequence): + xaxis = val_in_ns(sequence.tau2) + elif isinstance(sequence, CarrPurcellSequence): + xaxis = val_in_ns(sequence.step) func = lambda x, a, b, e: a*np.exp(-b*x**e) - xaxis = val_in_ns(sequence.tau2) + data = func(xaxis,1,2e-6,1.8) data = add_phaseshift(data, 0.05) return xaxis, data diff --git a/docsrc/source/API_docs.rst b/docsrc/source/API_docs.rst index 3aa9dd98..76f71ed9 100644 --- a/docsrc/source/API_docs.rst +++ b/docsrc/source/API_docs.rst @@ -25,6 +25,7 @@ Analysis Modules autodeer.ResPro.ResonatorProfileAnalysis autodeer.Relaxation.CarrPurcellAnalysis autodeer.Relaxation.ReptimeAnalysis + autodeer.Relaxation.RefocusedEcho2DAnalysis autodeer.DEER_analysis.DEERanalysis Sequences @@ -105,8 +106,11 @@ Optimisation .. autoapisummary:: autodeer.DEER_analysis.optimise_pulses + autodeer.DEER_analysis.calc_deer_settings + autodeer.DEER_analysis.calc_correction_factor autodeer.pulses.build_default_pulses autodeer.ResPro.optimise_spectra_position + I/O ~~~ diff --git a/docsrc/source/releasenotes.rst b/docsrc/source/releasenotes.rst index fd9e3d03..5826f74a 100644 --- a/docsrc/source/releasenotes.rst +++ b/docsrc/source/releasenotes.rst @@ -5,6 +5,13 @@ Version 0.8.0 (TBA): +++++++++++++++++++++++++++ - Major Support Update for 2D Decoherence - `RefocusedEcho2DSequence` reforumlated to support match normal style + - Added 2D Decoherence to the GUI + - Added `RefocusedEcho2DAnalysis` class +- Improvements to Advanced User Mode + - Added option to only measure up to RefocusedEcho2D +- New function `calc_deer_settings` to calculate DEER settings from the availiable relaxation data + + Version 0.7.0 (2024-04-01): +++++++++++++++++++++++++++ diff --git a/test/test_DEER_analysis.py b/test/test_DEER_analysis.py index 3d38de51..bd93f585 100644 --- a/test/test_DEER_analysis.py +++ b/test/test_DEER_analysis.py @@ -73,10 +73,6 @@ def test_IdentifyROI(): assert ROI[0] == 1.75 assert ROI[1] == 4.25 -@pytest.mark.notimplemented -def test_remove_echo(): - pass - def test_shift_pulse_freq_mono(): pulse = RectPulse(tp=16,freq=0,flipangle=np.pi/2) pulse_shifted = shift_pulse_freq(pulse, 0.1) @@ -151,3 +147,47 @@ def test_optimise_pulses(): fig = plot_overlap(fsweep, pump_pulse, exc_pulse, ref_pulse) assert fig is not None +from autodeer.hardware.dummy import _simulate_CP, _simulate_2D_relax +from autodeer.Relaxation import CarrPurcellAnalysis, RefocusedEcho2DAnalysis + +def get_CPAnalysis(): + seq = CarrPurcellSequence( + B=12220, LO=34.0, reptime=3e3,averages=1, shots=50, tau=10,n=2) + x,V = _simulate_CP(seq) + dataset = create_dataset_from_sequence(V,seq) + dataset.attrs['nAvgs'] = 1 + CP = CarrPurcellAnalysis(dataset) + CP.fit('double') + return CP + +def get_Ref2DAnalysis(): + seq = RefocusedEcho2DSequence( + B=12220, LO=34.0, reptime=3e3,averages=1, shots=50, tau=10,) + x,V = _simulate_2D_relax(seq) + dataset = create_dataset_from_sequence(V,seq) + dataset.attrs['nAvgs'] = 1 + Ref2D = RefocusedEcho2DAnalysis(dataset) + return Ref2D + + +@pytest.mark.parametrize("exp",['auto','5pDEER','4pDEER']) +def test_calc_deer_settings(exp): + CP_dataset = get_CPAnalysis() + Ref2D_dataset = get_Ref2DAnalysis() + + settings = calc_deer_settings(exp,CP_dataset, Ref2D_dataset) + + if exp == '5pDEER' or exp == 'auto': + assert settings['tau1'] == pytest.approx(2.478,rel=1e-3) + assert settings['tau2'] == pytest.approx(2.478,rel=1e-3) + assert settings['tau3'] == pytest.approx(0.3,abs=0.05) + assert settings['AimTime'] == pytest.approx(2,rel=1e-3) + assert settings['ExpType'] == '5pDEER' + elif exp == '4pDEER': + assert settings['tau1'] == pytest.approx(3.7,rel=1e-3) + assert settings['tau2'] == pytest.approx(7.5,rel=1e-3) + assert 'tau3' not in settings + assert settings['AimTime'] == pytest.approx(2,rel=1e-3) + assert settings['ExpType'] == '4pDEER' + + print(settings) \ No newline at end of file From ccb1d73b2386f6d30c80a1d68a6dcea2d8d9d5bf Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Fri, 3 May 2024 17:20:52 +0200 Subject: [PATCH 6/7] Refactured the launch function The code is now almost the same between the two --- autodeer/gui/main.py | 78 ++++++++++---------------------------------- 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/autodeer/gui/main.py b/autodeer/gui/main.py index 9a66963a..7b94889c 100644 --- a/autodeer/gui/main.py +++ b/autodeer/gui/main.py @@ -814,10 +814,7 @@ def update_T2(self,dataset=None): else: self.deer_settings['ESEEM'] = None main_log.info(f"No ESEEM detected") - - # Since the T2 values are not used for anything there is no need pausing the spectrometer - T2_worker = Worker(T2_process, dataset) T2_worker.signals.result.connect(self.refresh_T2) @@ -841,7 +838,7 @@ def check_T2(self, fitresult): self.worker.run_T2_relax(dt=new_dt) def check_CP(self, fitresult): - # Check if the T2 measurment is too short. + # Check if the CP measurment is too short. test_result = fitresult.check_decay() @@ -947,7 +944,7 @@ def timeout(self): QMessageBox.about(self,'Warning!', msg) main_log.warning(msg) - def RunFullyAutoDEER(self): + def RunAutoDEER(self, advanced=False): if self.spectromterInterface is None or self.connected is False: QMessageBox.about(self,'ERORR!', 'A interface needs to be connected first!') @@ -966,6 +963,14 @@ def RunFullyAutoDEER(self): self.userinput = userinput + if advanced: + self.deer_settings['ExpType'] = self.Exp_types.currentText() + self.deer_settings['tau1'] = self.Tau1Value.value() + self.deer_settings['tau2'] = self.Tau2Value.value() + self.deer_settings['tau3'] = self.Tau3Value.value() + else: + self.deer_settings = {'ExpType':'5pDEER','tau1':0,'tau2':0,'tau3':0} + # Block the autoDEER buttons self.FullyAutoButton.setEnabled(False) self.AdvancedAutoButton.setEnabled(False) @@ -980,6 +985,8 @@ def RunFullyAutoDEER(self): user_inputs=userinput, cores=self.cores ) self.starttime = time.time() + + self.worker.update_deersettings(self.deer_settings) self.worker.signals.status.connect(self.msgbar.setText) self.worker.signals.status.connect(main_log.info) @@ -1016,63 +1023,12 @@ def RunFullyAutoDEER(self): main_log.info(f"Starting autoDEER") return self.worker + + def RunFullyAutoDEER(self): + return self.RunAutoDEER(advanced=False) - def RunAdvanedAutoDEER(self): - - if self.spectromterInterface is None or self.connected is False: - QMessageBox.about(self,'ERORR!', 'A interface needs to be connected first!') - main_log.error('Could not run autoDEER. A interface needs to be connected first!') - return None - - if self.current_folder is None: - QMessageBox.about(self,'ERORR!', 'A folder needs to be selected first!') - main_log.error('Could not run autoDEER. A folder needs to be selected first!') - return None - - - - userinput = {} - userinput['MaxTime'] = self.MaxTime.value() - userinput['sample'] = self.SampleName.text() - userinput['Temp'] = self.TempValue.value() - userinput['ExpType'] = self.Exp_types.currentText() - userinput['tau1'] = self.Tau1Value.value() - userinput['tau2'] = self.Tau2Value.value() - userinput['tau3'] = self.Tau3Value.value() - userinput['ExcPulse'] = self.ExcPulseSelect.currentText() - userinput['RefPulse'] = self.RefPulseSelect.currentText() - userinput['PumpPulse'] = self.PumpPulseSelect.currentText() - userinput['DEER_update_func'] = self.q_DEER.refresh_deer - - # Block the autoDEER buttons - self.FullyAutoButton.setEnabled(False) - self.AdvancedAutoButton.setEnabled(False) - - self.waitCondition = QtCore.QWaitCondition() - mutex = QtCore.QMutex() - - - worker = autoDEERWorker( - self.spectromterInterface,wait=self.waitCondition,mutex=mutex, - results=self.current_results,LO=self.LO, gyro = self.gyro, - user_inputs=userinput, cores=self.cores) - worker.signals.status.connect(self.msgbar.setText) - worker.signals.fsweep_result.connect(self.update_fieldsweep) - worker.signals.respro_result.connect(self.update_respro) - worker.signals.optimise_pulses.connect(self.optimise_pulses) - worker.signals.relax_result.connect(self.update_relax) - worker.signals.quickdeer_result.connect(self.update_quickdeer) - worker.signals.quickdeer_update.connect(self.q_DEER.refresh_deer) - worker.signals.longdeer_update.connect(self.longDEER.refresh_deer) - - worker.signals.reptime_scan_result.connect(self.update_reptime) - - worker.signals.finished.connect(lambda: self.FullyAutoButton.setEnabled(True)) - worker.signals.finished.connect(lambda: self.AdvancedAutoButton.setEnabled(True)) - - self.worker = worker - self.threadpool.start(self.worker) - main_log.info(f"Starting autoDEER with user inputs") + def RunAdvancedAutoDEER(self): + return self.RunAutoDEER(advanced=True) def create_report(self): save_path = QFileDialog.getSaveFileName(self, 'Save File', self.current_folder, ".pdf") From d979d9a328959aa2f2698b32dd1b5aa3516c979b Mon Sep 17 00:00:00 2001 From: Hugo Karas Date: Fri, 3 May 2024 17:52:49 +0200 Subject: [PATCH 7/7] Relax2D in GUI --- autodeer/gui/autoDEER_worker.py | 11 +++- autodeer/gui/main.py | 113 ++++++++++++++++++++------------ 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/autodeer/gui/autoDEER_worker.py b/autodeer/gui/autoDEER_worker.py index 7245f60b..7618ad21 100644 --- a/autodeer/gui/autoDEER_worker.py +++ b/autodeer/gui/autoDEER_worker.py @@ -275,13 +275,20 @@ def run_deer(self,end_criteria,signal, dt=16,shot=50,averages=1000,): if ('tau1' in self.user_inputs) and (self.user_inputs['tau1'] != 0): tau1 = self.user_inputs['tau1'] tau2 = self.user_inputs['tau2'] - tau3 = self.user_inputs['tau3'] deertype = self.user_inputs['ExpType'] + if deertype == '5pDEER': + tau3 = self.user_inputs['tau3'] + else: + tau3 = None + elif self.deer_inputs != {}: tau1 = self.deer_inputs['tau1'] tau2 = self.deer_inputs['tau2'] - tau3 = self.deer_inputs['tau3'] deertype = self.deer_inputs['ExpType'] + if deertype == '5pDEER': + tau3 = self.deer_inputs['tau3'] + else: + tau3 = None dt = self.deer_inputs['dt'] else: diff --git a/autodeer/gui/main.py b/autodeer/gui/main.py index 7b94889c..fbb00130 100644 --- a/autodeer/gui/main.py +++ b/autodeer/gui/main.py @@ -228,15 +228,16 @@ def set_spectrometer_connected_light(self, state): light_pixmap = light_pixmap.scaledToHeight(30) self.Connected_Light.setPixmap(light_pixmap) - def load_folder(self): - file = str(QFileDialog.getExistingDirectory(self, "Select Directory",str(Path.home()))) - self.pathLineEdit.setText(file) - self.current_folder = file + def load_folder(self,*args, folder_path=None): + if folder_path is None: + folder_path = str(QFileDialog.getExistingDirectory(self, "Select Directory",str(Path.home()))) + self.pathLineEdit.setText(folder_path) + self.current_folder = folder_path setup_logs(self.current_folder) global main_log main_log = logging.getLogger('autoDEER') - main_log.info(f"Loading folder {file}") + main_log.info(f"Loading folder {self.current_folder}") def load_epr_file(self, store_location): @@ -689,47 +690,36 @@ def create_relax_figure(self): self.relax_ax = axs def refresh_relax_figure(self): - self.relax_ax.cla() - - fig = self.relax_canvas.figure - axs = self.relax_ax + - relax1D_results = [] - if 'relax' in self.current_results: - relax1D_results.append(self.current_results['relax']) - if 'T2_relax' in self.current_results: - relax1D_results.append(self.current_results['T2_relax']) + if 'relax2D' in self.current_results: + self.relax_ax[0].cla() + self.relax_ax[1].cla() + fig, axs = plt.subplots(2,1,figsize=(12.5, 6.28),layout='constrained',height_ratios=[2,1]) + relax_canvas = FigureCanvas(fig) + self.relax_canvas.figure.clear() + self.relax_v_left.replaceWidget(self.relax_canvas,relax_canvas) + self.relax_canvas = relax_canvas + self.relax_ax = axs + + self.current_results['relax2D'].plot2D(axs=self.relax_ax[0],fig=fig) + self.current_results['relax2D'].plot1D(axs=self.relax_ax[1],fig=fig) + else: + self.relax_ax.cla() + fig = self.relax_canvas.figure + axs = self.relax_ax + relax1D_results = [] + if 'relax' in self.current_results: + relax1D_results.append(self.current_results['relax']) + if 'T2_relax' in self.current_results: + relax1D_results.append(self.current_results['T2_relax']) - ad.plot_1Drelax(*relax1D_results,axs=axs,fig=fig,cmap=ad.primary_colors) + ad.plot_1Drelax(*relax1D_results,axs=axs,fig=fig,cmap=ad.primary_colors) - # axs.set_xlabel("Total Sequence Length ($\mu s$)") - # axs.set_ylabel("Signal (A. U.)") - - # if 'relax' in self.current_results: - # CP_results = self.current_results['relax'] - # CP_data = CP_results.data - # CP_data /= CP_data.max() - # if hasattr(CP_results, "fit_result"): - # axs.plot(CP_results.axis*4, CP_data, '.', label='CP', color='C1', ms=6) - # axs.plot(CP_results.axis*4, CP_results.func( - # CP_results.axis, *CP_results.fit_result[0]), label='CP-fit', color='C1', lw=2) - # else: - # axs.plot(CP_results.axis*4, CP_data, label='data', color='C1') - - # if 'T2_relax' in self.current_results: - # T2_results = self.current_results['T2_relax'] - # T2_data = T2_results.data - # T2_data /= T2_data.max() - # if hasattr(T2_results, "fit_result"): - # axs.plot(T2_results.axis*2, T2_data, '.', label='T2', color='C2', ms=6) - # axs.plot(T2_results.axis*2, T2_results.func( - # T2_results.axis, *T2_results.fit_result[0]), label='T2-fit', color='C2', lw=2) - # else: - # axs.plot(T2_results.axis*2, T2_data, label='data', color='C2') - - # axs.legend() self.relax_canvas.draw() + + def refresh_relax(self, fitresult): self.current_results['relax'] = fitresult @@ -789,10 +779,45 @@ def refresh_relax(self, fitresult): self.Tab_widget.setCurrentIndex(3) self.check_CP(fitresult) + + if self.worker is not None: + CP_decay = fitresult.func(fitresult.axis, *fitresult.fit_result[0]).data + # Find the index when CP_decay is below 0.05 + CP_decay = CP_decay/CP_decay[0] + CP_decay_bool = CP_decay < 0.05 + CP_decay_idx = np.where(CP_decay_bool)[0] + if len(CP_decay_idx) == 0: + CP_decay_idx = len(CP_decay) + else: + CP_decay_idx = CP_decay_idx[0] + max_tau = fitresult.axis[CP_decay_idx] + max_tau = ad.round_step(max_tau,1) + main_log.info(f"Max tau {max_tau:.2f} us") + self.worker.set_2D_max_tau(max_tau) if self.waitCondition is not None: # Wake up the runner thread self.waitCondition.wakeAll() + def update_relax2D(self, dataset=None): + if dataset is None: + dataset = self.current_data['relax2D'] + else: + self.current_data['relax2D'] = dataset + + # Since there is no fitting in the 2D data analysis it can be run in the main thread + + relax2DAnalysis = ad.RefocusedEcho2DAnalysis(dataset) + self.current_results['relax2D'] = relax2DAnalysis + + self.refresh_relax_figure() + + self.deer_settings = ad.calc_deer_settings('auto',self.current_results['relax'],self.current_results['relax2D'],self.aim_time,self.aim_MNR,self.waveform_precision) + self.deer_settings['dt'] = 16 + self.worker.update_deersettings(self.deer_settings) + + if self.waitCondition is not None: # Wake up the runner thread + self.waitCondition.wakeAll() + def update_T2(self,dataset=None): if dataset is None: @@ -999,6 +1024,10 @@ def RunAutoDEER(self, advanced=False): self.worker.signals.relax_result.connect(lambda x: self.save_data(x,'CP_Q')) self.worker.signals.T2_result.connect(self.update_T2) self.worker.signals.T2_result.connect(lambda x: self.save_data(x,'T2')) + + self.worker.signals.Relax2D_result.connect(self.update_relax2D) + self.worker.signals.Relax2D_result.connect(lambda x: self.save_data(x,'2D_DEC')) + self.worker.signals.quickdeer_result.connect(self.update_quickdeer) self.worker.signals.quickdeer_result.connect(lambda x: self.save_data(x,'DEER_5P_Q_quick')) self.worker.signals.quickdeer_update.connect(self.q_DEER.refresh_deer) @@ -1006,8 +1035,6 @@ def RunAutoDEER(self, advanced=False): self.worker.signals.longdeer_result.connect(lambda x: self.save_data(x,'DEER_5P_Q_long')) self.worker.signals.longdeer_result.connect(self.update_longdeer) - - self.worker.signals.reptime_scan_result.connect(self.update_reptime) self.worker.signals.timeout.connect(self.timeout)