Source code for mujpy.aux.aux

########################
# FFT AUTO PHASE METHODS
########################
[docs]def autops(data, fn, p0=0.0, p1=0.0): """ Automated phase correction from NMRglue by https://github.com/jjhelmus These functions provide support for automatic phasing of NMR data. ---------- Automatic linear phase correction Parameters ---------- data : ndarray Array of NMR data. fn : str or function Algorithm to use for phase scoring. Built in functions can be specified by one of the following strings: "acme", "peak_minima" p0 : float Initial zero order phase in degrees. p1 : float Initial first order phase in degrees. Returns ------- ndata : ndarray Phased NMR data. """ import numpy as np import scipy.optimize if not callable(fn): fn = { 'peak_minima': _ps_peak_minima_score, 'acme': _ps_acme_score, }[fn] opt = [p0, p1] opt = scipy.optimize.fmin(fn, x0=opt, args=(data, )) return ps(data, p0=opt[0], p1=opt[1]), opt[0], opt[1]
def _ps_acme_score(ph, data): """ Phase correction using ACME algorithm by Chen Li et al. Journal of Magnetic Resonance 158 (2002) 164-168 Parameters ---------- pd : tuple Current p0 and p1 values data : ndarray Array of NMR data. Returns ------- score : float Value of the objective function (phase score) """ import numpy as np stepsize = 1 phc0, phc1 = ph s0 = ps(data, p0=phc0, p1=phc1) data = np.real(s0) # Calculation of first derivatives ds1 = np.abs((data[1:]-data[:-1]) / (stepsize*2)) p1 = ds1 / np.sum(ds1) # Calculation of entropy p1[p1 == 0] = 1 h1 = -p1 * np.log(p1) h1s = np.sum(h1) # Calculation of penalty pfun = 0.0 as_ = data - np.abs(data) sumas = np.sum(as_) if sumas < 0: pfun = pfun + np.sum((as_/2) ** 2) p = 1000 * pfun return h1s + p def _ps_peak_minima_score(ph, data): """ Phase correction using simple minima-minimisation around highest peak This is a naive approach but is quick and often achieves reasonable results. The optimisation is performed by finding the highest peak in the spectra (e.g. TMSP) and then attempting to reduce minima surrounding it. Parameters ---------- pd : tuple Current p0 and p1 values data : ndarray Array of NMR data. Returns ------- score : float Value of the objective function (phase score) """ phc0, phc1 = ph s0 = ps(data, p0=phc0, p1=phc1) data = np.real(s0) i = np.argmax(data) mina = np.min(data[i-100:i]) minb = np.min(data[i:i+100]) return np.abs(mina - minb)
[docs]def ps(data, p0=0.0, p1=0.0, inv=False): """ Linear phase correction Parameters ---------- data : ndarray Array of NMR data. p0 : float Zero order phase in degrees. p1 : float First order phase in degrees. inv : bool, optional True for inverse phase correction Returns ------- ndata : ndarray Phased NMR data. """ import numpy as np p0 = p0 * np.pi / 180. # convert to radians p1 = p1 * np.pi / 180. size = data.shape[-1] apod = np.exp(1.0j * (p0 + (p1 * np.arange(size) / size))).astype(data.dtype) if inv: apod = 1 / apod return apod * data
############## # MUGUI AUX ##############
[docs]def derange(string): ''' derange(string) reads string assuming 2, 3 or 4 csv values, integers or floats, or 2, 3 or 4 space separated values, integers or floats returns 2, 3 or 4 floats, or two negative values, if the string does not contain commas or spaces ''' # print('In derange') try: try: values = [float(x) for x in string.split(',')] except: values = [float(x) for x in string.split('')] if len(values)==5: if values[3]<values[1] or values[4]>values[3] or values[2]>values[1] or values[1]<values[0] or sum(n<0 for n in values)>0: return -1,-1 return values[0],values[1],values[2],values[3],values[4] # start, stop, packe, last, packl if len(values)==4: return values[0],values[1],1,values[2],values[3] # start, stop,packe, last, packl elif len(values)==3: return values[0],values[1],values[2] # start, stop, pack elif len(values)==2: return values[0],values[1] # start, stop except: return -1,-1
[docs]def derange_int(string): ''' derange(string) reads string assuming 2, 3 or 4 csv values, integers or floats, or 2, 3 or 4 space separated values, integers or floats returns 2, 3 or 4 floats, or two negative values, if the string does not contain commas or spaces ''' # print('In derange_int') try: try: values = [int(x) for x in string.split(',')] except: values = [int(x) for x in string.split('')] if len(values)==5: if values[3]<values[1] or values[4]>values[3] or values[2]>values[1] or values[1]<values[0] or sum(n<0 for n in values)>0: return -1,-1 return values[0],values[1],values[2],values[3],values[4] # start, stop, packe, last, packl if len(values)==4: if values[3]<values[1] or values[2]>values[1] or values[1]<values[0] or sum(n<0 for n in values)>0: return -1,-1 return values[0],values[1],1,values[2],values[3] # start, stop, packe, last, packl elif len(values)==3: return values[0],values[1],values[2] # start, stop, pack elif len(values)==2: return values[0],values[1] # start, stop except: return -1,-1
[docs]def derun(string): ''' parses string, producing a list of runs; expects comma separated items looks for 'l','l:m','l+n+m' where l, m, n are integers rejects all other characters returns a list of lists of integer ''' s = [] try: # systematic str(int(b[])) to check that b[] ARE integers for b in string.split(','): # csv kcolon = b.find(':') # ':' and '+' are mutually exclusive kplus = b.find('+') if kcolon>0: # value might be a range for j in range(int(b[:kcolon]),int(b[kcolon+1:])+1): s.append([str(j)]) # strings elif kplus>0: ss = [] k0 = 0 while kplus>0: # str(int(b[])) ss.append(int(b[k0:kplus])) k0 = kplus+1 kplus = b.find('+',k0) ss.append(int(b[k0:])) s.append([str(q) for q in ss]) else:# value should be an integer int(b) # produces an Error if b is not an integer s.append([b]) # added as a string except Exception as e: s = [] errmsg = e if 'errmsg' not in locals(): errmsg = None return s, errmsg
[docs]def findall(p, s): '''Yields all the positions of the pattern p in the string s.''' i = s.find(p) while i != -1: yield i i = s.find(p, i+1)
[docs]def find_nth(haystack, needle, n): start = haystack.rfind(needle) while start >= 0 and n > 1: start = haystack.rfind(needle, 1, start-1) n -= 1 return start
[docs]def get_grouping(groupcsv): """ name = 'forward' or 'backward' grouping(name) is an np.array wth detector indices group.value[k] for k=0,1 is a shorthand csv like '1:3,5' or '1,3,5' etc. index is present mugui.mainwindow.selected_index out is mugui._output_ for error messages returns grouping, group, index group and index are changed only in case of errors """ import numpy as np # two shorthands: either a list, comma separated, such as 1,3,5,6 # or a pair of integers, separated by a colon, such as 1:3 = 1,2,3 # only one column is allowed, but 1, 3, 5 , 7:9 = 1, 3, 5, 7, 8, 9 # or 1:3,5,7 = 1,2,3,5,7 are also valid # get the shorthand from the gui Text groupcsv = groupcsv.replace('.',',') # can only be a mistake: '.' means ',' try: if groupcsv.find(':')==-1: # colon not found, csv only grouping = np.array([int(s) for s in groupcsv.split(',')]) else: # colon found if groupcsv.find(',')+groupcsv.find(':')==-2: grouping = np.array([int(groupcsv)]) elif groupcsv.find(',')+1: # True if found, False if not found (not found yields -1) firstcomma = groupcsv.index(',') lastcomma = groupcsv.rindex(',') if firstcomma < groupcsv.find(':'): # first read csv then range partial = np.array([int(s) for s in groupcsv[:lastcomma].split(',')]) fst = int(groupcsv[lastcomma:grouping.find(':')]) lst = int(groupcsv[groupcsv.find(':')+1:]) grouping[name] = np.concatenate((partial,arange(fst,lst+1,dtype=int))) else: # first read range then csv partial = np.array([int(s) for s in groupcsv[:lastcomma].split(',')]) fst = int(groupcsv[:groupcsv.find(':')]) lst = int(groupcsv[groupcsv.find(':')+1:firstcomma]) grouping = np.concatenate((np.arange(fst,lst+1,dtype=int),partial)) else: # only range fst = int(groupcsv[:groupcsv.find(':')]) lst = int(groupcsv[groupcsv.find(':')+1:]) grouping = np.arange(fst,lst+1,dtype=int) grouping -=1 # python starts at 0 except: grouping = np.array([-1]) return grouping
[docs]def get_title(run): ''' form standard psi title ''' return '{} {} {} {}'.format(run.get_sample(),run.get_field(),run.get_orient(),run.get_temp())
[docs]def muvalid(string,out,index): ''' parse function CHECK WITH MUCOMPONENT, THAT USES A DIFFERENT SCHEME accepted functions are RHS of agebraic expressions of parameters p[i], i=0...ntot ''' import re pattern = re.compile(r"\p\[(\d+)\]") # find all patterns p[*] where * is digits test = pattern.sub(r"a",string) # substitute "a" to "p[*]" in s # strindices = pattern.findall(string) # indices = [int(strindices[k]) for k in range(len(strindices))] # in internal parameter list # mindices = ... # produce the equivalent minuit indices valid = True message = '' try: safetry(test) # should select only safe use (although such a thing does not exist!) except Exception as e: with out: print('function: {}. Tested: {}. Wrong or not allowed syntax: {}'.format(string,test,e)) index = 3 valid = False return valid
[docs]def muvaluid(string): ''' Run suite fits: muvaluid returns True/False checks the syntax for string function corresponding to flag='l'. Meant for pars displaying large changes across the run suite, requiring different migrad start guesses. ''' # string syntax: e.g. "0.2*3,2.*4,20." # means that for the first 3 runs value = 0.2, # for the next 4 runs value = 2.0 # from the 8th run on value = 20.0 try: value_times_list = string.split(',') last = value_times_list.pop() for value_times in value_times_list: value,times = value_times.split('*') dum, dum = float(value),int(times) dum = float(last) return True except: return False
[docs]def muvalue(lrun,string): ''' Run suite fits: muvalue returns the value for the nint-th parameter of the lrun-th run according to string (corresponding flag='l'). Large parameter change across the run suite requires different migrad start guesses. ''' # string syntax: e.g. "0.2*3,2.*4,20." # means that for the first 3 runs value = 0.2, # for the next 4 runs value = 2.0 # from the 8th run on value = 20.0 value = [] for value_times in string.split(','): try: value,times = value_time.split('*') for k in range(int(times)): value.append(float(value)) except: for k in range(len(value),lrun): value.append(float(value_time)) return value[lrun]
[docs]def muzeropad(runs,out): ''' muzeropad(runs,out) runs is a string containing the run number utility of the suite tab, not a method future: 1) determine how many leading zeros for padding read a file from data dir check number of digits before '.' count number of digits in run zero pad now: 0) zeroth version pads a fixed number of zeros to 4 digits out in mugui is _output_, Textarea for printing messages ''' zeros='0000' if len(runs)<len(zeros): return zeros[:len(zeros)-len(runs)]+runs elif len(runs)==len(zeros): return runs else: with out: print('Too long run number!') return []
[docs]def norun_msg(out): with out: print('No run loaded yet! Load one first (select suite tab).')
[docs]def plotile(x,xdim=0,offset=0): ''' xt = plotile(x,xdim,xoffset=0) e.g. x.shape = (1,1000) y.shape = (4,1000) xt = plotile(x,4) yt = plotile(y,offset=0.1) ''' # x is an array(x.shape[0],x.shape[1]) # xoffset is a step offset # xdim = x.shape[0] if xdim == 0 else xdim # each row is shifted by xoffset*n, where n is the index of the row # # from copy import deepcopy from numpy import tile, arange xt = deepcopy(x) if xdim != 0: # x is a 1D array, must be tiled to xdim xt = tile(xt,(int(xdim),1)) if offset != 0: xt += tile(offset*arange(xt.shape[0]),(x.shape[1],1)).transpose() return xt
[docs]def rebin(x,y,strstp,pack,e=None): ''' x,y[,e] are 2D arrays to be rebinned pack is the rebinning factor, e.g: xb = array([x[0:pack].sum()/pack]) strstp = [start,stop] is a list of indices rebinning is done on slices of x,y[,e] such as x[start:stop] use either xb,yb = rebin(x,y,strstp,pack) or xb,yb,eyb = rebin(x,y,strstp,pack,ey) # the 5th is y error Works also with pack = 1 * Now works for 1d array x and 2d ndarrays y, ey xb, yb, eb are 2D arrays, e.g. xb.shape = (1,25000), yb.shape = (nruns,25000) ''' from numpy import floor, sqrt,empty, array start,stop = strstp if pack==1: # no rebinning, just a slice of 2D arrays xx = x[:,start:stop] yy = y[:,start:stop] # print('in rebin: shape xx {}, yy {}'.format(xx.shape,yy.shape)) if e is None: return xx,yy else: ee = e[:,start:stop] return xx,yy,ee else: m = int(floor((stop-start)/pack)) # length of rebinned xb mn = m*pack # length of x slice xx =x[:,start:start+mn] # slice of the first 2D array xx = xx.reshape(m,pack) # temporaty 2d array xb = array([xx.sum(1)/pack]) # rebinned first ndarray nruns = y.shape[0] # number of runs yb = empty((nruns,m)) if e is not None: eb = empty((nruns,m)) for k in range(nruns): # each row is a run yy = y[k][start:start+mn] # slice row yy = yy.reshape(m,pack) # temporaty 2d yb[k] = yy.sum(1)/pack # rebinned row if e is not None: ey = e[k][start:start+mn] # slSice row ey = ey.reshape(m,pack) # temporaty 2d eb[k] = sqrt((ey**2).sum(1))/pack # rebinned row if e is not None: return xb,yb,eb else: return xb,yb
[docs]def safetry(string): from math import acos,asin,atan,atan2,ceil,cos,cosh,degrees,e,exp,floor,log,log10,pi,pow,radians,sin,sinh,sqrt,tan,tanh safe_list = ['a','acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'floor', 'log', 'log10', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'] # use the list to filter the local namespace a = 0.3 safe_dict={} for k in safe_list: safe_dict[k]=locals().get(k) # print(safe_dict[k]) return eval(string,{"__builtins__":None},safe_dict)
[docs]def set_bar(n,b): ''' service to animate histograms e.g. in the fit tab extracted from matplotlib animate histogram example ''' from numpy import array, zeros, ones import matplotlib.path as path # get the corners of the rectangles for the histogram left = array(b[:-1]) right = array(b[1:]) bottom = zeros(len(left)) top = bottom + n nrects = len(left) # here comes the tricky part -- we have to set up the vertex and path # codes arrays using moveto, lineto and closepoly # for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the # CLOSEPOLY; the vert for the closepoly is ignored but we still need # it to keep the codes aligned with the vertices nverts = nrects*(1 + 3 + 1) verts = zeros((nverts, 2)) codes = ones(nverts, int) * path.Path.LINETO codes[0::5] = path.Path.MOVETO codes[4::5] = path.Path.CLOSEPOLY verts[0::5, 0] = left verts[0::5, 1] = bottom verts[1::5, 0] = left verts[1::5, 1] = top verts[2::5, 0] = right verts[2::5, 1] = top verts[3::5, 0] = right verts[3::5, 1] = bottom xlim = [left[0], right[-1]] return verts, codes, bottom, xlim
[docs]def translate(nint,lmin,function): string = function[nint].value # search for integers between '[' and ']' start = [i+1 for i in findall('[',string)] stop = [i for i in findall(']',string)] nints = [string[i:j] for (i,j) in zip(start,stop)] nmins = [lmin[int(string[i:j])] for (i,j) in zip(start,stop)] for lstr,m in zip(nints,nmins): string = string.replace(lstr,str(m)) return string
[docs]def value_error(value,error): ''' value_error(v,e) returns a string of the format v(e) ''' from numpy import floor, log10 exponent = int(floor(log10(error))) most_significant = int(round(error/10**exponent)) if most_significant>9: exponent += 1 most_significant=1 exponent = -exponent if exponent<0 else 0 form = '"{:.' form += '{}'.format(exponent) form += 'f}({})".format(value,most_significant)' return eval(form)