def test_background(): #======================================================================= "Check that dipolar kernel builds with the background included" t = np.linspace(0, 3, 80) r = np.linspace(2, 6, 100) B = bg_exp(t, 0.5) lam = 0.25 KBref = (1 - lam + lam * dipolarkernel(t, r, integralop=False)) * B[:, np.newaxis] KB = dipolarkernel(t, r, mod=lam, bg=B, integralop=False) assert np.all(abs(KB - KBref) < 1e-5)
def test_regularized_global(): #======================================================================= "Check global SNLLS of a nonlinear-constrained + linear-regularized problem" t1 = np.linspace(0, 3, 150) t2 = np.linspace(0, 4, 200) r = np.linspace(2.5, 5, 80) P = dd_gauss2(r, 3.7, 0.5, 0.5, 4.3, 0.3, 0.5) kappa = 0.50 lam1 = 0.25 lam2 = 0.35 K1 = dipolarkernel(t1, r, mod=lam1, bg=bg_exp(t1, kappa)) K2 = dipolarkernel(t2, r, mod=lam2, bg=bg_exp(t2, kappa)) V1 = K1 @ P V2 = K2 @ P # Global non-linear model def globalKmodel(par): # Unpack parameters kappa, lam1, lam2 = par K1 = dipolarkernel(t1, r, mod=lam1, bg=bg_exp(t1, kappa)) K2 = dipolarkernel(t2, r, mod=lam2, bg=bg_exp(t2, kappa)) return K1, K2 # Non-linear parameters # [kappa lambda1 lambda2] par0 = [0.5, 0.5, 0.5] lb = [0, 0, 0] ub = [1, 1, 1] # Linear parameters: non-negativity lbl = np.zeros(len(r)) ubl = [] # Separable LSQ fit fit = snlls([V1, V2], globalKmodel, par0, lb, ub, lbl, ubl, uq=False) Pfit = fit.lin assert ovl(P, Pfit) > 0.9
def test_filtered_savgol(): #============================================================ "Check estimation of noiselevel using a Savitzky-Golay filter" np.random.seed(1) t = np.linspace(0, 3, 200) r = np.linspace(2, 6, 100) P = dd_gauss(r, 3, 0.5) lam = 0.25 B = bg_exp(t, 1.5) noise = whitegaussnoise(t, 0.03) V = dipolarkernel(t, r, mod=lam, bg=B) @ P + noise truelevel = np.std(noise) approxlevel = noiselevel(V, 'savgol', 11, 3) assert abs(approxlevel - truelevel) < 1e-2
def test_der(): #============================================================ "Check estimation of noiselevel using a moving-mean filter" np.random.seed(1) t = np.linspace(0, 3, 200) r = np.linspace(2, 6, 100) P = dd_gauss(r, 3, 0.5) lam = 0.25 B = bg_exp(t, 1.5) noise = whitegaussnoise(t, 0.05) V = dipolarkernel(t, r, mod=lam, bg=B) @ P + noise truelevel = np.std(noise) approxlevel = noiselevel(V, 'der') assert abs(approxlevel - truelevel) < 1e-2
def test_reference(): #============================================================ "Check estimation of noiselevel using a reference signal" np.random.seed(1) t = np.linspace(0, 3, 200) r = np.linspace(2, 6, 100) P = dd_gauss(r, 3, 0.5) lam = 0.25 B = bg_exp(t, 1.5) Vref = dipolarkernel(t, r, mod=lam, bg=B) @ P noise = whitegaussnoise(t, 0.03) V = Vref + noise truelevel = np.std(noise) approxlevel = noiselevel(V, 'reference', Vref) assert abs(approxlevel - truelevel) < 1e-2
def test_phenomenological(): #================================================================================== "Check that phenomenological background models are propely used" t = np.linspace(0, 5, 150) kappa = 0.3 lam = 0.5 #Reference Bref = bg_exp(t, 0.3) #Output Bmodel = lambda t: bg_hom3d(t, kappa, 1) path = [[], []] path[0] = [1 - lam] path[1] = [lam, 0] B = dipolarbackground(t, path, Bmodel) assert max(abs(B - Bref) < 1e-8)
def test_complex(): #============================================================ "Check estimation of noiselevel using a complex signal" t = np.linspace(0, 3, 200) r = np.linspace(2, 6, 100) P = dd_gauss(r, 4, 0.4) lam = 0.25 B = bg_exp(t, 1.5) np.random.seed(1) noise = whitegaussnoise(t, 0.03) np.random.seed(2) noisec = 1j * whitegaussnoise(t, 0.03) V = dipolarkernel(t, r, mod=lam, bg=B) @ P Vco = V * np.exp(-1j * np.pi / 5) Vco = Vco + noise + noisec truelevel = np.std(noise) approxlevel = noiselevel(Vco, 'complex') assert abs(truelevel - approxlevel) < 1e-2
def correctscale(V, t, tmax=[], model='deer'): r""" Amplitude scale correction Takes the experimental dipolar signal (V) on a given time axis (t) and rescales it so that it is 1 at time zero, taking into account the noise. It fits a model specified in (model) over the interval ``abs(t)<tmax`` around time zero. It returns the rescaled signal (Vc) and the scaling factor (V0). Parameters ---------- V : array_like Experimenal signal. t : array_like Time axis, in microseconds. tmax : scalar, optional Time cutoff for fit range, in microseconds. model : string, optional Model to fit over ``abs(t)<tmax`` * ``'deer'`` - DEER model with Gaussian distribution. * ``'gauss'`` - Raised Gaussian. The default is ``'deer'`` Returns ------- Vc : ndarray Rescaled signal. """ if not all(np.isreal(V)): raise ValueError('Input signal cannot be complex.') if isempty(tmax): tmax = max(t) # Approximate scaling Amp0 = max(V) V_ = V / Amp0 # Limit fit to region around time zero idx = abs(t) <= tmax V_ = V_[idx] t_ = t[idx] if model == 'deer': # DEER model with Gaussian distribution if len(V_) < 5: raise KeyError( 'Number of points in fit range cannot be smaller than number of model parameters. Increase tmax.' ) r = np.linspace(0.5, 20, 300) fitmodel = lambda p: p[0] * (dipolarkernel( t_, r, mod=p[1], bg=bg_exp(t_, p[4])) @ dd_gauss(r, *p[[2, 3]])) # parameters: amplitude, mod depth, Gauss position, Gauss std, conc par0 = [1, 0.5, 3, 0.3, 0.2] lb = [1e-3, 0.01, 0, 0.01, 0] ub = [10, 1, 20, 5, 100] elif model == 'gauss': # Gaussian function if len(V_) < 3: raise KeyError( 'Number of points in fit range cannot be smaller than number of model parameters. Increase tmax.' ) fitmodel = lambda p: (p[0] - p[1]) + p[1] * np.exp(-t_**2 / p[2]**2) par0 = [1, 1, 1] lb = [1e-3, 1e-3, 1e-3] ub = [10, 1000, 10000] else: raise KeyError(f"Unknown model '{model}'") # Run the parametric model fitting fit = snlls(V_, fitmodel, par0, lb, ub, uq=False, lin_frozen=[1]) # Get the fitted signal amplitude and scale the signal V0 = Amp0 * fit.param[0] Vc = V / V0 return Vc
def globalKmodel(par): # Unpack parameters kappa, lam1, lam2 = par K1 = dipolarkernel(t1, r, mod=lam1, bg=bg_exp(t1, kappa)) K2 = dipolarkernel(t2, r, mod=lam2, bg=bg_exp(t2, kappa)) return K1, K2