def dist_wrap(l1, l2, norm_index=2): """ return the average distance modulo 2pi between two lists of angles The average is calculated as an n-norm. >>> round(dist_wrap([2, 1, 0], [-4, 1, 3]), 7) 3.013336 """ diffs = modtwopi(np.array(l1) - np.array(l2), offset=0) return np.linalg.norm(diffs, norm_index)
def plot_array_phase(freq, ax=plt.gca(), fixp=False, ref_probe=0, ref_offset=0): """ Warning - this really should have FS passed as an arg Also, to fix 2Pi skips - if there are a number of noisy graphs that should be similar. Choose the 2pi skip that keeps a graph closest to an already processed one (or the ensemble average?) (how??) """ if not isinstance(freq, int): # convert reals to the corresponding index findex = np.argsort(np.abs(freqs - freq))[0] print('freq {freq} index {findex}'.format(**locals())), else: findex = freq phaselist_orig = [np.angle(FTs[isig][findex]) for isig in isigs] phaselist = np.copy(phaselist_orig) # save the original for debugging phaselist = fix2pi_skips(phaselist) if fixp else phaselist phaselist = phaselist - phaselist[ref_probe] if ref_probe > -999 else phaselist if (ref_probe) > -999 and (not fixp): # use mod2pi to phaselist = modtwopi(phaselist, offset=ref_offset) ax.plot(phaselist, label=str('{f:.1f}kHz'.format(f=freqs[findex]/1e3))) ax.legend(fontsize='xx-small', loc='best',ncol=1 + min(3, len(isigs)//6)) plt.show(block=0)
def dists(a, instances, mask = None, method='euler'): """ calculate the RMS (not RSS) distance of the phase set to all phase sets based on std in new_mode_identify presently takes 1.4 secs for 1Mx14 float32.(100ns/elt 4ns +,*, 21ns sqrt 43 ns modtwopi 26n (new fmod version) """ if (len(a) != np.shape(instances)[-1]): raise ValueError('shape of phases does not match shape of sample') if mask is None: mask=np.arange(len(a)) if not(hasattr(instances, 'std')): print('make phase_array into an np array to speed up 100x') # the following assumes it is a structured array - it may be a list~!~ instances = np.array(instances.tolist()) cc = np.tile(a[mask], (np.shape(instances)[0],1)) # next line is a little faster # x=sum(modtwopi((phases[:,sel]-subset[clinds[cl][0]]),offset=0)**2,1) # # time x=sqrt(average(modtwopi((phases[0:1000000].astype(float32)+1)**2),1)) # float32 should be faster than float or float16 (worst) but # because subset is float32, it is promoted to that precision automatically sq = (modtwopi(instances[:,mask]-a, offset=0))**2 return(np.sqrt(np.average(sq,len(np.shape(instances))-1)))
def fsplot_phase(input_data, closed=True, ax=None, hold=0, offset=0, block=False): """ plot the phase of a flucstruc, optionally inserting the first point at the end (if closed=True). Applies to closed arrays (e.g complete 2pi). Until Feb 2013, this version did not yet attempt to take into account angles, or check that adjacent channels are adjacent (i.e. ch2-ch1, ch2-c2 etc). Channel names are taken from the fs and plotted abbreviated 1/1/2011: TODO This appears to work only for database = None config 1/17/2011: bdb: May be fixed - I had used channel instead of channel.name """ # extract by channels ch1n,ch2n,ch21n,dp = [],[],[],[] # bdb this line should be replaced by a call to a routine names something #like <plotted_width> to help in deciding if the label will fit on the #current graph. if ax is None: ax=pl.gca() # why is this repeated below max_chars = get_axes_pixcells(ax)[2]/8 # assuming an 10 pt font is 8 wide. if (2*len(input_data.dphase) *(2+len(input_data.dphase[0].channel_1.name)))> max_chars: sep = '\n-' else: sep = '-' #sep = '-' # 2013 change order from ch1-ch2 to ch2-ch1 = ch2 - ch1 for dpn in input_data.dphase: ch1n.append(dpn.channel_1.name) ch2n.append(dpn.channel_2.name) ch21n.append(dpn.channel_2.name+sep+dpn.channel_1.name) dp.append(dpn.delta) min_length = max(1,40/len(input_data.channels)) # min_length - min_length??? short_names_1,p,s = split_names(ch1n, min_length=2) # need to break up loops to do this short_names_2,p,s = split_names(ch2n, min_length=5) # # need to know how big the shortened names are before deciding on the separator if (2*len(input_data.dphase)*(2+len(short_names_1[0])))> max_chars: sep = '\n-' else: sep = '-' ch21n = [ch2n[i]+sep+ch1n[i] for i in range(len(ch1n))] short_ch21n = [short_names_2[i]+sep+short_names_1[i] for i in range(len(short_names_1))] # correct result for a diag that doesn MP1,2,3,4,5,6,1, and not closed #ch21n = ['MP2-MP1', 'MP3-MP2', 'MP4-MP3', 'MP5-MP4', 'MP6-MP5', 'MP1-MP6'] # if closed, and only 1-6 in the file, want the same! if closed: # ch2 will be MP1, ch1 will be MP6 MP1-MP6 ch1n.append(ch2n[-1]) ch2n.append(ch1n[0]) ch21n.append(ch2n[-1]+sep+ch1n[0]) short_ch21n.append(short_names_2[-1]+sep+short_names_1[0]) #dp.insert(0,dp[-1]) # closed means use the phase diff from the ends # sign bug fixed here - made closed opposite sign to open.. dp=np.append(dp,modtwopi(-np.sum(dp),offset=offset)) #dp = fix2pi_skips(dp, around=offset) dp = modtwopi(dp, offset=offset) if hold == 0: ax.clear() # This is a kludgy way to read coordinates. Should be through acquisition.base or acquisition.'device' Phi = np.array([2*np.pi/360*float(pyfusion.config.get ('Diagnostic:{cn}'. format(cn=c.name), 'Coords_reduced') .split(',')[0]) for c in input_data.channels]) Theta = np.array([2*np.pi/360*float(pyfusion.config.get ('Diagnostic:{cn}'. format(cn=c.name), 'Coords_reduced') .split(',')[1]) for c in input_data.channels]) if closed: Phi = np.append(Phi, Phi[0]) Theta = np.append(Theta, Theta[0]) if len(np.unique(Theta)) > 1: AngName = 'Theta' Ang = Theta dAngfor1 = np.pi/13 #dAngfor1 is the average coil dph for M=1 span = (np.pi)/13 # span is the average coil spacing in radians else: AngName = 'Phi' Ang = Phi dAngfor1 = (2*np.pi)/6 span = 2*np.pi/6 # expect Phi from ~.2 to twopi+.2 (if closed) Angfix = fix2pi_skips(Ang,around=3.5) # Here use fix2pi_skips, as we want the coil location angle to # increase monotonically - the raw numbers in .cfg may not start at zero # need to make sure the right dp is divided by the right dPhi! ax.plot(fix2pi_skips(Angfix[0:-1],around=np.pi),dp/np.diff(Angfix)*span, '+-',label='dp/d'+AngName[0]) ax.plot(fix2pi_skips(Angfix[0:-1],around=np.pi),dp,'o-', label='d'+AngName) ax.set_xlim([ax.get_xlim()[0], ax.get_xlim()[1]+np.average(np.diff(Angfix))]) ax.plot(ax.get_xlim(),[0,0],':k',linewidth=0.5) for N in (-3,-2,-1,1,2,3): ax.plot(ax.get_xlim(),[N*dAngfor1,N*dAngfor1],':r',linewidth=0.5) tot=np.sum(dp) over = Angfix[-1] - Angfix[0] print(tot) if closed: # only makes sense to draw a mean dp line if closed ax.plot(ax.get_xlim(),[tot/(2*np.pi),tot/(2*np.pi)],':b',linewidth=0.5) ax.legend(prop=FontProperties(size='small')) ax.set_title('sum = {s:.2f} over {o:.2f} ~ {r:.1f}' .format(s=tot,o=over, r=tot/over)) debug_(pyfusion.DEBUG, 1, key='fs_phase') #ax.set_xticks(range(len(dp))) ax.set_xticks(Angfix) ax.set_xticklabels(short_ch21n) pl.show(block=block)
from pyfusion.data.DA_datamining import DA from pyfusion.acquisition.read_text_pyfusion import read_text_pyfusion, plot_fs_DA, merge_ds from pyfusion.visual.window_manager import rmw, cmw, omw, lmw, smw from pyfusion.utils.utils import fix2pi_skips, modtwopi from pyfusion.visual.sp import off, on, tog from pyfusion.data.convenience import between, bw, btw, decimate, his, broaden dd=DA('W7X_MIR/DAMIRNOV_41_13_nocache_15_3m_2018fl.zip') dd.info() fig4, axs4=plt.subplots(4,1) figfs, axf=plt.subplots(1,1) plot_fs_DA(dd, ax=axf) w6=where((btw(dd['freq'],4,8)) & (dd['amp']>0.0015) & (dd['t_mid']>3.2) &(dd['shot'] == 180904035))[0] plot_fs_DA(dd, ax=axf, inds=w6,marker='s',ms=300,alpha=0.5) axs4[0].plot(array([modtwopi(ph,offset=0) for ph in dd['phases'][w6]]).T,'c',lw=.5) w6=where((btw(dd['freq'],4,8)) & (dd['amp']>0.015) & (dd['t_mid']>3.2) &(dd['shot'] == 180904035))[0] axs4[0].plot(array([modtwopi(ph,offset=0) for ph in dd['phases'][w6]]).T,'b',lw=.5) axs4[0].set_title('4-8kHz, after 3.2s') w0=where((btw(dd['freq'],0,2)) & (dd['amp']>0.0015) & (dd['t_mid']>3.2) &(dd['shot'] == 180904035))[0] plot_fs_DA(dd, ax=axf, inds=w0,marker='s',ms=300,alpha=0.5) axs4[1].plot(array([modtwopi(ph,offset=0) for ph in dd['phases'][w0]]).T,'b',lw=.5) axs4[1].set_title('0-2kHz, > 3.2s') w6e=where((btw(dd['freq'],4,12)) & (dd['amp']>0.015) & (dd['t_mid']<3.2) &(dd['shot'] == 180904035))[0] plot_fs_DA(dd, ax=axf, inds=w6e,marker='^',ms=300,alpha=0.5) axs4[2].plot(array([modtwopi(ph,offset=0) for ph in dd['phases'][w6e]]).T,'c',lw=.5) w6e=where((btw(dd['freq'],4,12)) & (dd['amp']>0.025) & (dd['t_mid']<3.2) &(dd['shot'] == 180904035))[0] axs4[2].plot(array([modtwopi(ph,offset=0) for ph in dd['phases'][w6e]]).T,'b',lw=1) axs4[2].set_title('4-12kHz, before 3.2s') axs4[3].plot(array([modtwopi(ph,offset=.5) for ph in dd['phases']])[::5].T,'b',lw=.015) axs4[3].set_title('all')
def dist2(a,b, method='euler'): ax = (max(len(np.shape(a)),len(np.shape(b)))) - 1 return(np.sum((modtwopi((np.array(a)- np.array(b)),offset=0)**2),ax))
if DAfile != '': da = DA(DAfile, load=1) else: print('try for a da in locals()') try: da except NameError as reason: print(' no da - did you use run -i?') wh = da['indx'] # all of them #wh = np.where((btw(da['freq'],2,4)) & (da['amp']>0.012) )[0] wh = np.where(da['shot'] == 180904035)[0] phs = da['phases'][wh].tolist() inds = da['indx'][wh].tolist() ctrs = np.mean(modtwopi(phs, 0), 0) num = len(ctrs) print(len(wh)) for left in range(len(phs), min_left, -1): if version == 1: ctrs = np.mean(modtwopi(phs, 0), 0) dists = [dist_wrap(ctrs, phases) / num for phases in phs] if np.max(dists) < tolerance: break worst = np.argmax(dists) phs.pop(worst) inds.pop(worst) """ worst = np.argsort(dists)[::-1] # this won't work as position in list changes....
def fsplot_phase(input_data, closed=True, ax=None, hold=0, offset=0, AngName=None, block=False): """ plot the phase of a flucstruc, optionally inserting the first point at the end (if closed=True). Applies to closed arrays (e.g complete 2pi). Until Feb 2013, this version did not yet attempt to take into account angles, or check that adjacent channels are adjacent (i.e. ch2-ch1, ch2-c2 etc). Channel names are taken from the fs and plotted abbreviated 1/1/2011: TODO This appears to work only for database = None config 1/17/2011: bdb: May be fixed - I had used channel instead of channel.name """ # extract by channels ch1n, ch2n, ch21n, dp = [], [], [], [] # bdb this line should be replaced by a call to a routine names something #like <plotted_width> to help in deciding if the label will fit on the #current graph. if ax is None: ax = pl.gca() # why is this repeated below max_chars = get_axes_pixcells( ax)[2] / 8 # assuming an 10 pt font is 8 wide. if (2 * len(input_data.dphase) * (2 + len(input_data.dphase[0].channel_1.name))) > max_chars: sep = '\n-' else: sep = '-' #sep = '-' # 2013 change order from ch1-ch2 to ch2-ch1 = ch2 - ch1 for dpn in input_data.dphase: ch1n.append(dpn.channel_1.name) ch2n.append(dpn.channel_2.name) ch21n.append(dpn.channel_2.name + sep + dpn.channel_1.name) dp.append(dpn.delta) min_length = max(1, 40 / len(input_data.channels)) # min_length - min_length??? short_names_1, p, s = split_names( ch1n, min_length=2) # need to break up loops to do this short_names_2, p, s = split_names(ch2n, min_length=4) # used to be 5 # need to know how big the shortened names are before deciding on the separator if (2 * len(input_data.dphase) * (2 + len(short_names_1[0]))) > max_chars: sep = '\n-' else: sep = '-' ch21n = [ch2n[i] + sep + ch1n[i] for i in range(len(ch1n))] short_ch21n = [ short_names_2[i] + sep + short_names_1[i] for i in range(len(short_names_1)) ] # correct result for a diag that doesn MP1,2,3,4,5,6,1, and not closed #ch21n = ['MP2-MP1', 'MP3-MP2', 'MP4-MP3', 'MP5-MP4', 'MP6-MP5', 'MP1-MP6'] # if closed, and only 1-6 in the file, want the same! if closed: # ch2 will be MP1, ch1 will be MP6 MP1-MP6 ch1n.append(ch2n[-1]) ch2n.append(ch1n[0]) ch21n.append(ch2n[-1] + sep + ch1n[0]) short_ch21n.append(short_names_2[-1] + sep + short_names_1[0]) #dp.insert(0,dp[-1]) # closed means use the phase diff from the ends # sign bug fixed here - made closed opposite sign to open.. dp = np.append(dp, modtwopi(-np.sum(dp), offset=offset)) #dp = fix2pi_skips(dp, around=offset) dp = modtwopi(dp, offset=offset) if hold == 0: ax.clear() # This is a kludgey way to read coordinates. Should be through acquisition.base or acquisition.'device' Phi = np.array([ 2 * np.pi / 360 * float( pyfusion.config.get('Diagnostic:{cn}'.format(cn=c.config_name), 'Coords_reduced').split(',')[0]) for c in input_data.channels ]) Theta = np.array([ 2 * np.pi / 360 * float( pyfusion.config.get('Diagnostic:{cn}'.format(cn=c.config_name), 'Coords_reduced').split(',')[1]) for c in input_data.channels ]) if closed: Phi = np.append(Phi, Phi[0]) Theta = np.append(Theta, Theta[0]) # kludgey - needed to deal with Shaun's array - should # really convert form cylindrical if AngName is not 'Phi' and len(np.unique(Theta)) > 1: AngName = 'Theta' Ang = Theta dAngformeq1 = np.pi / 13 #dAngformeq1 is the average coil dph for M=1 span = (np.pi) / 13 # span is the average coil spacing in radians else: AngName = 'Phi' Ang = Phi dAngformeq1 = (2 * np.pi) / 6 # seems hardwired for LHD or shaun?? span = 2 * np.pi / 6 if closed: dAngformeq1 = (2 * np.pi) / len(Ang) # bdb 2019 - overide if closed span = dAngformeq1 # why two veraible for the same thing - are they same? # expect Phi from ~.2 to twopi+.2 (if closed) Angfix = fix2pi_skips(Ang, around=3.5) # Here use fix2pi_skips, as we want the coil location angle to # increase monotonically - the raw numbers in .cfg may not start at zero # need to make sure the right dp is divided by the right dPhi! ax.plot( fix2pi_skips(Angfix[0:-1], around=np.pi), dp / np.diff(Angfix) * span, '+-', label='dp/d' + AngName[0], lw=0.2, ) ax.plot(fix2pi_skips(Angfix[0:-1], around=np.pi), dp, 'o-', label='d' + AngName) ax.set_xlim( [ax.get_xlim()[0], ax.get_xlim()[1] + np.average(np.diff(Angfix))]) ax.plot(ax.get_xlim(), [0, 0], ':k', linewidth=0.5) for N in (-3, -2, -1, 1, 2, 3): ax.plot(ax.get_xlim(), [N * dAngformeq1, N * dAngformeq1], ':m', linewidth=0.2, label=['', '~dth(m=1)'][N == 1]) tot = np.sum(dp) over = Angfix[-1] - Angfix[0] print(tot) if closed: # only makes sense to draw a mean dp line if closed ax.plot(ax.get_xlim(), [tot / (2 * np.pi), tot / (2 * np.pi)], ':b', linewidth=0.5) ax.legend(prop=FontProperties(size='xx-small')) ax.set_title('sum = {s:.2f} over {o:.2f} ~ {r:.1f}'.format(s=tot, o=over, r=tot / over)) debug_(pyfusion.DEBUG, 1, key='fs_phase') #ax.set_xticks(range(len(dp))) ax.set_xticks(Angfix) # think this out better - e.g. this 128 should be adjusted according to max_chars ax.set_xticklabels(short_ch21n, fontsize=min(128 // len(short_ch21n), 10), rotation=['horizontal', 'vertical'][len(Angfix) > 8]) print('block = ', block) pl.show(block=block)