def onclick(self, event): if event.button is not self.mousebutton: return if event.xdata == None: event.xdata = 0 event.ydata = 0 self.pts.append((event.xdata, event.ydata)) try: color = self.kwargs['color'] except KeyError: color=self.colors[(len(self.pts)-1)\ %len(self.colors)] #Colors will cycle try: fontsize = self.kwargs['fontsize'] except KeyError: fontsize = 14 l = self.ax.plot([event.xdata], [event.ydata], 'o', color=color)[0] self.ax.text(event.xdata,event.ydata,len(self.pts),fontsize=fontsize,\ bbox=dict(facecolor='white', alpha=0.25),\ horizontalalignment='center',\ verticalalignment='bottom') plt.draw() # Quit the loop if we achieve the desired number of points # if self.max_pts and len(self.pts) == self.max_pts: event.key = 'quit_for_sure' self.onquit(event) if self.verbose: Logger.write('You are now on point #%i' % (len(self.pts) + 1))
def __init__(self, mousebutton=1, max_pts=1, ax=None, cbar=None, message=None, verbose=False, **kwargs): import time self.pts = [] self.verbose = verbose self.kwargs = kwargs self.mousebutton = mousebutton self.max_pts = max_pts self.colors = ['b', 'r', 'g', 'c', 'm', 'y'] self.cid1 = plt.gcf().canvas.mpl_connect('button_press_event', self.onclick) self.cid2 = plt.gcf().canvas.mpl_connect('key_press_event', self.ondelete) self.cid3 = plt.gcf().canvas.mpl_connect('key_press_event', self.onquit) self.cid4 = plt.gcf().canvas.mpl_connect('key_press_event', self.colorlimits) if not ax: ax = plt.gca() self.ax = ax self.cbar = cbar if verbose: if not message: message = 'Please click %s points on the current figure using \ mouse button %i. You may at any time strike the "c" \ key to change color limits on an image, or the "delete" \ key to remove the last selected point.' % ( max_pts, mousebutton) Logger.write(message) Logger.write('\tYou are now on point #1.') plt.gcf().canvas.start_event_loop(timeout=0)
def GenerateCarbonylTips(Ls=numpy.linspace(30,20e3,100),a=30,\ wavelength=6e3,taper_angles=[10,20],\ geometries=['cone','hyperboloid']): skin_depth = .05 if wavelength is None: freq = 0 else: freq = a / numpy.float(wavelength) Ls = Ls / numpy.float(a) for i, geometry in enumerate(geometries): for k, taper_angle in enumerate(taper_angles): for j, L in enumerate(Ls): Logger.write('Currently working on geometry "%s", L/a=%s...' % (geometry, L)) tip.build_charge_distributions(Nqs=144,Nzs=144,L=L,taper_angle=taper_angle,quadrature='TS',\ geometry=geometry,freq=freq,skin_depth=skin_depth) progress = ( (i * len(taper_angles) + k) * len(Ls) + j + 1) / numpy.float( len(Ls) * len(geometries) * len(taper_angles)) * 100 Logger.write('\tProgress: %1.1f%%' % progress) Logger.write('Done!')
def polyfit_poles_residues(self,deg=6,zmax=10): Nterms=self.Ps.shape[1] Rs=self.Rs.cslice[:zmax] Ps=self.Ps.cslice[:zmax] zs=Rs.axes[0] if self.verbose: Logger.write('Finding complex polynomial approximations of degree %i '%deg+\ 'to the first %i poles and residues, up to a value z/a=%s...'%(Nterms,zmax)) self.Ppolys=[] for i in range(Nterms): Ppoly=numpy.polyfit(zs,Ps[:,i],deg=deg) self.Ppolys.append(Ppoly) self.Rpolys=[] for i in range(Nterms): Rpoly=numpy.polyfit(zs,Rs[:,i],deg=deg) self.Rpolys.append(Rpoly)
gtuples = numpy.vstack((x, g, g)).transpose().tolist() btuples = numpy.vstack((x, b, b)).transpose().tolist() cdit = {'red': rtuples, 'green': gtuples, 'blue': btuples} pyplot.register_cmap(name=cmap_name, data=cdit) r = r[::-1] g = g[::-1] b = b[::-1] rtuples_r = numpy.vstack((x, r, r)).transpose().tolist() gtuples_r = numpy.vstack((x, g, g)).transpose().tolist() btuples_r = numpy.vstack((x, b, b)).transpose().tolist() cdit_r = {'red': rtuples_r, 'green': gtuples_r, 'blue': btuples_r} pyplot.register_cmap(name=cmap_name + '_r', data=cdit_r) Logger.write('Registered colormaps "%s" and "%s_r"...' % ((cmap_name, ) * 2)) # ----- Plotting functions _color_index_ = 0 all_colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'teal', 'gray', 'navy'] def next_color(): global _color_index_ color = all_colors[_color_index_ % len(all_colors)] _color_index_ += 1 return color
def invert_signal(self,signals,nodes=[(1,0)],Nterms=10,\ interpolation='linear',\ select_by='continuity',\ closest_pole=0,\ scaling=10): """The inversion is not unique, consequently the selected solution will probably be wrong if signal values correspond with "beta" values that are too large (`|beta|~>min{|Poles|}`). This can be expected to break at around `|beta|>2`.""" #Default is to invert signal in contact #~10 terms seem required to converge on e.g. SiO2 spectrum, #especially on the Re(beta)<0 (low signal) side of phonons global roots,poly,root_scaling #global betas,all_roots,pmin,rs,ps,As,Bs,roots,to_minimize if self.verbose: Logger.write('Inverting `signals` based on the provided `nodes` to obtain consistent beta values...') if not hasattr(signals,'__len__'): signals=[signals] if not isinstance(signals,AWA): signals=AWA(signals) zs,ws=list(zip(*nodes)) ws_grid=numpy.array(ws).reshape((len(ws),1)) #last dimension is to broadcast over all `Nterms` equally zs=numpy.array(zs) Rs=self.Rs[:,:Nterms].interpolate_axis(zs,axis=0,kind=interpolation, bounds_error=False,extrapolate=True) Ps=self.Ps[:,:Nterms].interpolate_axis(zs,axis=0,kind=interpolation, bounds_error=False,extrapolate=True) #`rs` and `ps` can safely remain as arrays for `invres` rs=(Rs*ws_grid).flatten() ps=Ps.flatten() k0=numpy.sum(rs/ps).tolist() #Rescale units so their order of magnitude centers around 1 rscaling=numpy.exp(-(numpy.log(numpy.abs(rs).max())+\ numpy.log(numpy.abs(rs).min()))/2.) pscaling=numpy.exp(-(numpy.log(numpy.abs(ps).max())+\ numpy.log(numpy.abs(ps).min()))/2.) root_scaling=1/pscaling #rscaling=1 #pscaling=1 if self.verbose: Logger.write('\tScaling residues by a factor %1.2e to reduce floating point overflow...'%rscaling) Logger.write('\tScaling poles by a factor %1.2e to reduce floating point overflow...'%pscaling) rs*=rscaling; ps*=pscaling k0*=rscaling/pscaling signals=signals*rscaling/pscaling #highest order first in `Ps` and `Qs` #VERY SLOW - about 100ms on practical inversions (~60 terms) As,Bs=invres(rs, ps, k=[k0], tol=1e-16, rtype='avg') #tol=1e-16 is the smallest allowable to `unique_roots`.. dtype=numpy.complex128 #Double precision offers noticeable protection against overflow As=numpy.array(As,dtype=dtype) Bs=numpy.array(Bs,dtype=dtype) signals=signals.astype(dtype) #import time betas=[] for i,signal in enumerate(signals): #t1=time.time() #Root finding `roots` seems to give noisy results when `Bs` has degree >84, with dynamic range ~1e+/-30 in coefficients... #Pretty fast - 5-9 ms on practical inversions with rank ~60 companion matrices, <1 ms with ~36 terms #@TODO: Root finding chokes on `Nterms=9` (number of eigenfields) and `Nts=12` (number of nodes), # necessary for truly converged S3 on resonant phonons, probably due to # floating point overflow - leading term increases exponentially with # number of terms, leading to huge dynamic range. # Perhaps limited by the double precision of DGEEV. # So, replace with faster / more reliable root finder? # We need 1) speed, 2) ALL roots (or at least the first ~10 smallest) poly=As-signal*Bs roots=find_roots(poly,scaling=scaling) roots=roots[roots.imag>0] roots*=root_scaling #since all beta units scaled by `pscaling`, undo that here #print time.time()-t1 #How should we select the most likely beta among the multiple solutions? #1. Avoids large changes in value of beta if select_by=='difference' and i>=1: if i==1 and self.verbose: Logger.write('\tSelecting remaining roots by minimizing differences with prior...') to_minimize=numpy.abs(roots-betas[i-1]) #2. Avoids large changes in slope of beta (best for spectroscopy) #Nearly guarantees good beta spectrum, with exception of very loosely sampled SiC spectrum #Loosely samples SiO2-magnitude phonons still perfectly fine elif select_by=='continuity' and i>=2: if i==2 and self.verbose: Logger.write('\tSelecting remaining roots by ensuring continuity with prior...') earlier_diff=betas[i-1]-betas[i-2] current_diffs=roots-betas[i-1] to_minimize=numpy.abs(current_diffs-earlier_diff) #3. Select specifically which pole we want |beta| to be closest to else: if i==0 and self.verbose: Logger.write('\tSeeding inversion closest to pole %i...'%closest_pole) reordering=numpy.argsort(numpy.abs(roots)) #Order the roots towards increasing beta roots=roots[reordering] to_minimize=numpy.abs(closest_pole-numpy.arange(len(roots))) beta=roots[to_minimize==to_minimize.min()].squeeze() betas.append(beta) if not i%5 and self.verbose: Logger.write('\tProgress: %1.2f%% - Inverted %i signals of %i.'%\ (((i+1)/numpy.float(len(signals))*100),\ (i+1),len(signals))) betas=AWA(betas); betas.adopt_axes(signals) betas=betas.squeeze() if not betas.ndim: betas=betas.tolist() return betas
def __init__(self,zs=numpy.logspace(-3,2,400),Nterms_max=20,sort=True, *args,**kwargs): """For some reason using Nqs>=244, getting higher q-resolution, only makes more terms relevant, requiring twice as many terms for stability and smoothness in approach curves... (although overall """ self.zs=zs self.Nterms_max=Nterms_max #Take a look at current probe geometry #Adjust quadrature parameters that assist in yielding smooth residues/poles #Pre-determined as good values for Nterms=10 geometry=tip.LRM.geometric_params['geometry'] if self.verbose: Logger.write('Setting up eigenfield tip model for geometry "%s"...'\ %geometry) if geometry is 'cone': tip.LRM.quadrature_params['b']=.5 elif geometry is 'hyperboloid': tip.LRM.quadrature_params['b']=.5 global Rs,Ps Rs=[] Ps=[] for i,z in enumerate(zs): if i==0: kwargs['reload_signal']=True elif i==1: kwargs['reload_signal']=False d=get_tip_eigenbasis_expansion(z,a=1,*args,**kwargs) Rrow=d['Rs'] Prow=d['Ps'] Rrow[numpy.isnan(Rrow)]=Rrow[numpy.isfinite(Rrow)][-1] Prow[numpy.isnan(Prow)]=Prow[numpy.isfinite(Prow)][-1] Rrow=Rrow[Prow.real>0] Prow=Prow[Prow.real>0] where_unphys_Ps=(Prow.imag>0) Prow[where_unphys_Ps]-=1j*Prow[where_unphys_Ps].imag Prow=Prow[:50] Rrow=Rrow[:50] if i>1 and sort: #Ensure continuity with previous poles (could have done residues instead, but this works) sorting=numpy.array([numpy.argmin( numpy.abs((Prow-previous_P)-\ (previous_P-preprevious_P))) for previous_P,preprevious_P in zip(previous_Prow,preprevious_Prow)]) Prow=Prow[sorting] Rrow=Rrow[sorting] Rs.append(Rrow[:Nterms_max]) Ps.append(Prow[:Nterms_max]) #Make sure to keep a reference to previous set of poles and residues if sort: previous_Prow=Prow previous_Rrow=Rrow if i>=1: preprevious_Prow=previous_Prow preprevious_Rrow=previous_Rrow terms=numpy.arange(Nterms_max)+1 self.Rs=AWA(Rs,axes=[zs,terms],axis_names=['z/a','Term']) self.Ps=AWA(Ps,axes=[zs,terms],axis_names=['z/a','Term']) ##Remove `nan`s from poles and residues## #Best way to remove `nan`s (=huge values) is to replace with largest finite value #Largest such value in Rs will be found in ratio to that in Ps, implying that #beta in denominator of that term is irrelevant, so term+offset goes to zero anyway.. for j in range(Nterms_max): Rrow=self.Rs[:,j]; Prow=self.Ps[:,j] Rrow[numpy.isnan(Rrow)]=Rrow[numpy.isfinite(Rrow)][-1] #Highest value will be for largest z (end of array) Prow[numpy.isnan(Prow)]=Prow[numpy.isfinite(Prow)][-1] ##Remove any positive imaginary part from poles## #These are unphysical and are perhaps a by-product of inaccurate eigenvalues #when diagonalizing an almost-singular matrix (i.e. g*Lambda) #Just put them on the real line, at least it doesn't seem to hurt anything, at most it's more physical. where_unphys_Ps=(self.Ps.imag>0) self.Ps[where_unphys_Ps]-=1j*self.Ps[where_unphys_Ps].imag if self.verbose: Logger.write('\tDone.')
def CompareCarbonylTips(Ls=numpy.linspace(30,20e3,100),a=30,\ wavelength=6e3,amplitude=60,lift=30,\ geometries=['cone','hyperboloid'],\ taper_angles=[20],\ load=True,save=True,demo_file='CarbonylTips.pickle',\ enhancement_index=2): carbonyl_freqs = numpy.linspace(1730, 1750, 10) normalize_at_freq = numpy.mean(carbonyl_freqs) freqs = [a / numpy.float(wavelength) ] #load frequency should be quite close to `sio2_freq` Ls = Ls / float(a) freq_labels = ['ED'] skin_depth = .05 ################################################## ## Load or compute Carbonyl measurement metrics ## ################################################## global d d_keys=['max_s3','max_s2',\ 'max_rel_absorption','max_absorption','max_freq',\ 'charges0','charges_q0','Lambda0','enhancement',\ 'signals','norm_signals','DCSD_contact','DCSD_lift',\ 'psf_contact','psf_lift'] demo_path = os.path.join(root_dir, demo_file) if load and os.path.isfile(demo_path): Logger.write('Loading lengths data...') d = pickle.load(open(demo_path)) else: tip.LRM.load_params['reload_model'] = True tip.LRM.geometric_params['skin_depth'] = skin_depth d = {} for i, geometry in enumerate(geometries): for n, taper_angle in enumerate(taper_angles): #If geometry is not conical, do not iterate to additional taper angles, these are all the same. if geometry not in ['PtSi', 'hyperboloid', 'cone'] and n > 0: break tip.LRM.geometric_params['geometry'] = geometry tip.LRM.geometric_params['taper_angle'] = taper_angle signals_for_geometry = {} for j, freq in enumerate(freqs): signals_for_freq = dict([(key, []) for key in d_keys]) for k, L in enumerate(Ls): tip.LRM.geometric_params['L'] = int(L) Logger.write( 'Currently working on geometry "%s", freq=%s, L/a=%s...' % (geometry, freq, L)) #Make sure to calculate on zs all the way up to zmax=2*amplitude+lift carbonyl_vals=tip.LRM(carbonyl_freqs,rp=Carbonyl.reflection_p,Nqs=72,zmin=.1,a=a,amplitude=amplitude+lift/2.,\ normalize_to=mat.Si.reflection_p,normalize_at=normalize_at_freq,load_freq=freq,Nzs=30) ##Get overall signal with approach curve## global carbonyl_sigs carbonyl_sigs = carbonyl_vals['signals'] * numpy.exp( -1j * numpy.angle( carbonyl_vals['norm_signals'].cslice[0])) carbonyl_rel_sigs = carbonyl_vals[ 'signals'] / carbonyl_vals['norm_signals'].cslice[0] ind = numpy.argmax(carbonyl_rel_sigs.imag.cslice[0] ) #Find maximum phase in contact signals_for_freq['max_s3'].append( carbonyl_vals['signal_3'] [ind]) #Pick out the peak frequency signals_for_freq['max_s2'].append( carbonyl_vals['signal_2'] [ind]) #Pick out the peak frequency signals_for_freq['max_rel_absorption'].append( carbonyl_rel_sigs[:, ind].imag.cslice[0] ) #Relative to out-of-contact on reference signals_for_freq['max_absorption'].append( carbonyl_sigs[:, ind].imag.cslice[0] ) #Absolute signal signals_for_freq['max_freq'].append( carbonyl_freqs[ind]) signals_for_freq['charges0'].append( tip.LRM.charges0 / (2 * numpy.pi * tip.LRM.charge_radii)) signals_for_freq['charges_q0'].append( tip.LRM.charges.cslice[0] / (2 * numpy.pi * tip.LRM.charge_radii)) signals_for_freq['Lambda0'].append(tip.LRM.Lambda0) signals_for_freq['enhancement'].append( numpy.abs(2 * tip.LRM.charges0[enhancement_index])) signals_for_freq['signals'].append( carbonyl_sigs[:, ind]) #Pick out the peak frequency signals_for_freq['norm_signals'].append( carbonyl_vals['norm_signals']) signals_for_freq['DCSD_contact'].append( get_DCSD(carbonyl_sigs[:, ind], zmin=0.1, amplitude=60, lift=0)) signals_for_freq['DCSD_lift'].append( get_DCSD(carbonyl_sigs[:, ind], zmin=0.1, amplitude=60, lift=lift)) ##Isolate some point-spread functions## rs = numpy.linspace(0, 5, 200).reshape((200, 1)) integrand_contact=AWA(tip.LRM.qxs**2*\ special.j0(tip.LRM.qxs*rs)*tip.LRM.get_dipole_moments(tip.LRM.qxs)*\ tip.LRM.wqxs,axes=[rs.squeeze(),tip.LRM.qxs]) integrand_contact=integrand_contact.interpolate_axis(numpy.logspace(numpy.log(tip.LRM.qxs.min())/numpy.log(10),\ numpy.log(tip.LRM.qxs.max())/numpy.log(10),1000),\ axis=1) psf_contact = numpy.sum(integrand_contact, axis=1) signals_for_freq['psf_contact'].append( AWA(psf_contact, axes=[rs.squeeze()], axis_names=['r/a'])) integrand_lift=AWA(tip.LRM.qxs**2*numpy.exp(-tip.LRM.qxs*lift/numpy.float(a))*\ special.j0(tip.LRM.qxs*rs)*tip.LRM.get_dipole_moments(tip.LRM.qxs)*\ tip.LRM.wqxs,axes=[rs.squeeze(),tip.LRM.qxs]) integrand_lift=integrand_lift.interpolate_axis(numpy.logspace(numpy.log(tip.LRM.qxs.min())/numpy.log(10),\ numpy.log(tip.LRM.qxs.max())/numpy.log(10),1000),\ axis=1) psf_lift = numpy.sum(integrand_lift, axis=1) signals_for_freq['psf_lift'].append( AWA(psf_lift, axes=[rs.squeeze()], axis_names=['r/a'])) progress = (i * len(Ls) * len(freqs) + j * len(Ls) + k + 1) / numpy.float( len(Ls) * len(freqs) * len(geometries)) * 100 Logger.write('\tProgress: %1.1f%%' % progress) for key in list(signals_for_freq.keys()): if numpy.array( signals_for_freq[key] ).ndim == 2: #Prepend probe length axis and expand these into AWA's axes = [ a * Ls / numpy.float(wavelength), signals_for_freq[key][0].axes[0] ] axis_names = [ '$L/\lambda_\mathrm{C=O}$', signals_for_freq[key][0].axis_names[0] ] signals_for_freq[key]=AWA(numpy.array(signals_for_freq[key],dtype=numpy.complex),\ axes=axes,axis_names=axis_names) else: signals_for_freq[key]=AWA(numpy.array(signals_for_freq[key],dtype=numpy.complex),\ axes=[a*Ls/numpy.float(wavelength)],\ axis_names=['$L/\lambda_\mathrm{C=O}$']) signals_for_geometry[freq_labels[j]] = signals_for_freq geometry_name = geometry if geometry in ['PtSi', 'hyperboloid', 'cone']: geometry_name += str(taper_angle) d[geometry_name] = signals_for_geometry if not load and save: Logger.write('Saving tip comparison data...') file = open(demo_path, 'wb') pickle.dump(d, file) file.close() return d