def get_flat_top(shot=54196, times=None, smooth_dt = None, maxddw = None, hold=0, debug=0): """ debug=1 gives a plot """ if times==None: times=np.linspace(0.02,8,8020) ; from pyfusion.data.signal_processing import smooth bp=get_basic_diagnostics(shot=shot,diags=['w_p','dw_pdt','b_0'],times=times) # assume sign is OK - at the moment, the code to fix sign is in merge # but it is inactive. Probably should be in get_basic_diag.. # so far, it seems that w_p and i_p are corrected - not sure # about other flux loops. w_p = bp['w_p'] dw = bp['dw_pdt'] w=np.where(w_p < 1e6)[0] # I guess this is to exclude nans len(w) cent = np.sum(w_p[w]*times[w])/np.sum(w_p[w]) icent = np.where(times > cent)[0][0] print("centroid = {0:.1f}".format(cent)) if maxddw == None: maxddw = 100 if smooth_dt==None: smooth_dt = 0.1 # smooth for 0.05 sec dt = (times[1]-times[0]) ns = int(smooth_dt/dt) smootharr = [ns,ns,ns] offs = len(smootharr*ns) # correction for smoothing offset dwsm = smooth(dw,n_smooth=smootharr) # smooth dwdt ddw = np.diff(dwsm)/dt #second deriv # work away from the centroid until 2nd deriv exceeds maxddw # assume 100kJ /sec is ramp, and a change of this over a second wb = int(0.5*offs) + np.nanargmax(dwsm) we = int(0.1*offs) + np.nanargmin(dwsm) # wpmax = np.nanmax(w_p) # used to be maxddw - too restrictive now try dwsm wgtrev = np.where(np.abs(dwsm[icent-offs/2::-1])> maxddw*wpmax/100)[0] wgtfor = np.where(np.abs(dwsm[icent-offs/2:])> maxddw*wpmax/100)[0] if (len(wgtrev) < 10) or (len(wgtfor) < 10): print('*** flat_top not found on shot {s}'.format(s=shot)) return (0,0,(0,0,0,0,0)) wbf = icent - wgtrev[0] wef = icent + wgtfor[0] if debug>0: pl.plot(w_p,label='w_p',hold=hold) pl.plot(ddw,label='ddw') pl.plot(dwsm,linewidth=3,label='sm(dw)') pl.plot(dw/10,label='dw_pdt/10') pl.scatter([wb, wbf, icent, wef, we],[0,200,300,250,275]) pl.plot([wb,we],[0,0],label='b--e') pl.plot([wbf,wef],np.ones(2)*maxddw*wpmax/100,'o-',linewidth=2,label='bf-ef') pl.ylim(np.array([-1.1,1.1])*max(abs(dwsm))) pl.title(shot) pl.legend() debug_(max(pyfusion.DEBUG, debug),2, key='flat_top') #return(times[wb], times[we],(wb,we,wbf,wef,icent)) # used to ignore wbf,wef return(times[wbf], times[wef],(wb,we,wbf,wef,icent))
def get_flat_top(shot=54196, times=None, smooth_dt=None, maxddw=None, hold=0, debug=0): if times is None: times = np.linspace(0.02, 8, 8020) from pyfusion.data.signal_processing import smooth bp = get_basic_params(shot=shot, diags=['w_p', 'dw_pdt', 'b_0'], times=times) # assume get_basic corrects the sign w_p = bp['w_p'] dw = bp['dw_pdt'] w = np.where(w_p < 1e6)[0] len(w) cent = np.sum(w_p[w] * times[w]) / np.sum(w_p[w]) icent = np.where(times > cent)[0][0] print("centroid = {0:.1f}".format(cent)) if maxddw is None: maxddw = 100 if smooth_dt is None: smooth_dt = 0.1 # smooth for 0.05 sec dt = (times[1] - times[0]) ns = int(smooth_dt / dt) smootharr = [ns, ns, ns] offs = len(smootharr * ns) dwsm = smooth(dw, n_smooth=smootharr) ddw = np.diff(dwsm) / dt # work away from the centroid until 2nd deriv exceeds maxddw # assume 100kJ /sec is ramp, and a chage of this over a second wb = int(0.5 * offs) + np.nanargmax(dwsm) we = int(0.1 * offs) + np.nanargmin(dwsm) # wbf = offs + np.where(np.abs(ddw[0:icent]) > maxddw)[0][-1] wef = offs + icent + np.where(np.abs(ddw[icent:]) > maxddw)[0][0] if debug > 0: pl.plot(w_p, label='w_p', hold=hold) pl.plot(dwsm, label='sm(dw)') pl.plot(ddw / 10, label='ddw/10') pl.plot(dw, label='dw_pdt)') pl.scatter([wb, wbf, icent, wef, we], [0, 500, 1000, 1500, 2000]) pl.plot([wb, we], [0, 0], label='b--e') pl.ylim(array([-1.1, 1.1]) * max(abs(dwsm))) pl.title(shot) pl.legend() debug_(max(pyfusion.DEBUG, debug), key='flat_top') return (times[wb], times[we], (wb, we, wbf, wef, icent))
def get_flat_top(shot=54196, times=None, smooth_dt = None, maxddw = None, hold=0, debug=0): if times is None: times=np.linspace(0.02,8,8020) ; from pyfusion.data.signal_processing import smooth bp=get_basic_params(shot=shot,diags=['w_p','dw_pdt','b_0'],times=times) # assume get_basic corrects the sign w_p = bp['w_p'] dw = bp['dw_pdt'] w=np.where(w_p < 1e6)[0] len(w) cent = np.sum(w_p[w]*times[w])/np.sum(w_p[w]) icent = np.where(times > cent)[0][0] print("centroid = {0:.1f}".format(cent)) if maxddw is None: maxddw = 100 if smooth_dt is None: smooth_dt = 0.1 # smooth for 0.05 sec dt = (times[1]-times[0]) ns = int(smooth_dt/dt) smootharr = [ns,ns,ns] offs = len(smootharr*ns) dwsm = smooth(dw,n_smooth=smootharr) ddw = np.diff(dwsm)/dt # work away from the centroid until 2nd deriv exceeds maxddw # assume 100kJ /sec is ramp, and a chage of this over a second wb = int(0.5*offs) + np.nanargmax(dwsm) we = int(0.1*offs) + np.nanargmin(dwsm) # wbf = offs + np.where(np.abs(ddw[0:icent])> maxddw)[0][-1] wef = offs + icent + np.where(np.abs(ddw[icent:])> maxddw)[0][0] if debug>0: pl.plot(w_p,label='w_p',hold=hold) pl.plot(dwsm,label='sm(dw)') pl.plot(ddw/10,label='ddw/10') pl.plot(dw,label='dw_pdt)') pl.scatter([wb, wbf, icent, wef, we],[0,500,1000,1500,2000]) pl.plot([wb,we],[0,0],label='b--e') pl.ylim(array([-1.1,1.1])*max(abs(dwsm))) pl.title(shot) pl.legend() debug_(max(pyfusion.DEBUG, debug), key='flat_top') return(times[wb], times[we],(wb,we,wbf,wef,icent))
def restore_sin(data, t_range=None, chan=None, method=2, sweep_freq= 500, Vpp=90*2, clip_level_minus=-88, verbose=1): """ Restore the clipped part of the sinusoid - see examples/restore_sin for the old script version sweep_freq: sinusoidal sweep freq in Hz Vpp: pp value of signal before it was clipped clip_level_minus: value to ensure even soft clipping is excluded method: so far only 2 """ if t_range is not None: rd = data.reduce_time(t_range) else: rd = data.copy() # so fudge below will work # a big fudge to allow this to work on one channel!!! beware if chan is not None: rd.signal = rd.signal[chan] rd.channels = rd.channels[chan] if verbose>0: rd.plot_signals() # fourier filter to retain the fundamental - the amplitude with be reduced by the clipping stopband = sweep_freq * np.array([0.8,1.2]) passband = sweep_freq * np.array([0.9,1.1]) fd = rd.filter_fourier_bandpass(stopband=stopband,passband=passband) # calculate the time-varying amplitude of the filtered sinusoid amp = np.abs(analytic_signal(fd.signal))/np.sqrt(2) # normalise to one amp = amp/np.average(amp) if verbose > 0: fig, ax1 = plt.subplots(1, 1) if verbose > 0: ax1.plot(rd.timebase, rd.signal, 'b', label='orig', linewidth=.3) if verbose > 1: ax1.plot(rd.timebase, fd.signal, 'g', label='filtered', linewidth=.3) if method == 1: ax1.plot(rd.timebase, fd.signal/amp, 'm', label='corrected', linewidth=.3) ax1.plot(rd.timebase, 50*(1.3*amp-2.1) + 1.2*fd.signal/amp, 'r', label='corrected') # not bad, but try making the amplitude constant first, then take the # difference , excluding the clipped part, boxcar averaged over a # small, whole number of periods for i in range(2): # iterate to restore amplitude to a constant # first the reconstructed amplitude reconst = 1.0 * fd.signal # make a copy of the signal amprec = np.abs(analytic_signal(reconst)) reconst = Vpp/2.0 * reconst/amprec if method == 2: if verbose > 0: ax1.plot(rd.timebase, reconst, 'm', label='reconst before DC adjust') # should have a very nice constant ampl. sinusoid # now blank out the clipped, use given value because amplifier clipping # is 'soft', so automatic detection of clipping is not simple. wc = np.where(rd.signal < clip_level_minus)[0] weight = 1 + 0*reconst weight[wc] = 0 period = int(round(data.timebase.sample_freq/sweep_freq)) from pyfusion.data.signal_processing import smooth # iterate to make waves match where there is no clipping for i in range(6): err = rd.signal - reconst err[wc] = 0 if verbose > 0: print('average error {e:.3g}'.format(e=float(np.sum(err)/np.sum(weight)))) corrn = np.cumsum(err[0:-period]) - np.cumsum(err[period:]) divisor = np.cumsum(weight[0:-period]) - np.cumsum(weight[period:]) wnef = np.where(divisor <= 100)[0] divisor[wnef] = 100 if verbose > 1: ax1.plot(rd.timebase, reconst, '--', label='reconst, offset {i}'.format(i=i)) # reconst[period//2:-period//2] = reconst[period//2:-period//2] - corrn/divisor reconst[period//2:1-period//2] = reconst[period//2:1-period//2] + smooth(err,period)/smooth(weight,period) # plot(smooth(err,period)/smooth(weight,period)) debug_(pyfusion.DEBUG, 1, key='restore_sin') if verbose>0: ax1.plot(rd.timebase, reconst,'r', label='reconst, final offset') ax1.legend() fig.show() return(reconst)
weight[wc] = 0 period = 1000 from pyfusion.data.signal_processing import smooth # iterate to make waves match where there is no clipping for i in range(6): err = rd.signal[0] - reconst err[wc] = 0 print(sum(err) / sum(weight)) corrn = np.cumsum(err[0:-period]) - np.cumsum(err[period:]) divisor = np.cumsum(weight[0:-period]) - np.cumsum(weight[period:]) wnef = np.where(divisor <= 100)[0] divisor[wnef] = 100 if verbose > 1: ax1.plot(rd.timebase, reconst, '--', label='reconst, offset {i}'.format(i=i)) # reconst[period//2:-period//2] = reconst[period//2:-period//2] - corrn/divisor reconst[period // 2:1 - period // 2] = reconst[period // 2:1 - period // 2] + smooth( err, period) / smooth(weight, period) # plot(smooth(err,period)/smooth(weight,period)) debug_(pyfusion.DEBUG, 1, key='restore_sin') if method == 2: if verbose > 0: ax1.plot(rd.timebase, reconst, 'r', label='reconst, final offset') ax1.legend() fig.show()
def get_flat_top(shot=54196, times=None, smooth_dt=None, maxddw=None, hold=0, debug=0): """ debug=1 gives a plot """ if times is None: times = np.linspace(0.02, 8, 8020) from pyfusion.data.signal_processing import smooth bp = get_basic_diagnostics(shot=shot, diags=['w_p', 'dw_pdt', 'b_0'], times=times) # assume sign is OK - at the moment, the code to fix sign is in merge # but it is inactive. Probably should be in get_basic_diag.. # so far, it seems that w_p and i_p are corrected - not sure # about other flux loops. w_p = bp['w_p'] dw = bp['dw_pdt'] w = np.where(w_p < 1e6)[0] # I guess this is to exclude nans len(w) cent = np.sum(w_p[w] * times[w]) / np.sum(w_p[w]) icent = np.where(times > cent)[0][0] print("centroid = {0:.1f}".format(cent)) if maxddw is None: maxddw = 100 if smooth_dt is None: smooth_dt = 0.1 # smooth for 0.05 sec dt = (times[1] - times[0]) ns = int(smooth_dt / dt) smootharr = [ns, ns, ns] offs = len(smootharr * ns) # correction for smoothing offset dwsm = smooth(dw, n_smooth=smootharr) # smooth dwdt ddw = np.diff(dwsm) / dt #second deriv # work away from the centroid until 2nd deriv exceeds maxddw # assume 100kJ /sec is ramp, and a change of this over a second wb = int(0.5 * offs) + np.nanargmax(dwsm) we = int(0.1 * offs) + np.nanargmin(dwsm) # wpmax = np.nanmax(w_p) # used to be maxddw - too restrictive now try dwsm wgtrev = np.where( np.abs(dwsm[icent - offs / 2::-1]) > maxddw * wpmax / 100)[0] wgtfor = np.where( np.abs(dwsm[icent - offs / 2:]) > maxddw * wpmax / 100)[0] if (len(wgtrev) < 10) or (len(wgtfor) < 10): print('*** flat_top not found on shot {s}'.format(s=shot)) return (0, 0, (0, 0, 0, 0, 0)) wbf = icent - wgtrev[0] wef = icent + wgtfor[0] if debug > 0: pl.plot(w_p, label='w_p', hold=hold) pl.plot(ddw, label='ddw') pl.plot(dwsm, linewidth=3, label='sm(dw)') pl.plot(dw / 10, label='dw_pdt/10') pl.scatter([wb, wbf, icent, wef, we], [0, 200, 300, 250, 275]) pl.plot([wb, we], [0, 0], label='b--e') pl.plot([wbf, wef], np.ones(2) * maxddw * wpmax / 100, 'o-', linewidth=2, label='bf-ef') pl.ylim(np.array([-1.1, 1.1]) * max(abs(dwsm))) pl.title(shot) pl.legend() debug_(max(pyfusion.DBG(), debug), 2, key='flat_top') #return(times[wb], times[we],(wb,we,wbf,wef,icent)) # used to ignore wbf,wef return (times[wbf], times[wef], (wb, we, wbf, wef, icent))
def restore_sin(data, t_range=None, chan=None, method=2, sweep_freq=500, Vpp=90 * 2, clip_level_minus=-88, verbose=1): """ Restore the clipped part of the sinusoid - see examples/restore_sin for the old script version sweep_freq: sinusoidal sweep freq in Hz Vpp: pp value of signal before it was clipped clip_level_minus: value to ensure even soft clipping is excluded method: so far only 2 """ if t_range is not None: rd = data.reduce_time(t_range) else: rd = data.copy() # so fudge below will work # a big fudge to allow this to work on one channel!!! beware if chan is not None: rd.signal = rd.signal[chan] rd.channels = rd.channels[chan] if verbose > 0: rd.plot_signals() # fourier filter to retain the fundamental - the amplitude with be reduced by the clipping stopband = sweep_freq * np.array([0.8, 1.2]) passband = sweep_freq * np.array([0.9, 1.1]) fd = rd.filter_fourier_bandpass(stopband=stopband, passband=passband) # calculate the time-varying amplitude of the filtered sinusoid amp = np.abs(analytic_signal(fd.signal)) / np.sqrt(2) # normalise to one amp = amp / np.average(amp) if verbose > 0: fig, ax1 = plt.subplots(1, 1) if verbose > 0: ax1.plot(rd.timebase, rd.signal, 'b', label='orig', linewidth=.3) if verbose > 1: ax1.plot(rd.timebase, fd.signal, 'g', label='filtered', linewidth=.3) if method == 1: ax1.plot(rd.timebase, fd.signal / amp, 'm', label='corrected', linewidth=.3) ax1.plot(rd.timebase, 50 * (1.3 * amp - 2.1) + 1.2 * fd.signal / amp, 'r', label='corrected') # not bad, but try making the amplitude constant first, then take the # difference , excluding the clipped part, boxcar averaged over a # small, whole number of periods for i in range(2): # iterate to restore amplitude to a constant # first the reconstructed amplitude reconst = 1.0 * fd.signal # make a copy of the signal amprec = np.abs(analytic_signal(reconst)) reconst = Vpp / 2.0 * reconst / amprec if method == 2: if verbose > 0: ax1.plot(rd.timebase, reconst, 'm', label='reconst before DC adjust') # should have a very nice constant ampl. sinusoid # now blank out the clipped, use given value because amplifier clipping # is 'soft', so automatic detection of clipping is not simple. wc = np.where(rd.signal < clip_level_minus)[0] weight = 1 + 0 * reconst weight[wc] = 0 period = int(round(data.timebase.sample_freq / sweep_freq)) from pyfusion.data.signal_processing import smooth # iterate to make waves match where there is no clipping for i in range(6): err = rd.signal - reconst err[wc] = 0 if verbose > 0: print('average error {e:.3g}'.format( e=float(np.sum(err) / np.sum(weight)))) corrn = np.cumsum(err[0:-period]) - np.cumsum(err[period:]) divisor = np.cumsum(weight[0:-period]) - np.cumsum(weight[period:]) wnef = np.where(divisor <= 100)[0] divisor[wnef] = 100 if verbose > 1: ax1.plot(rd.timebase, reconst, '--', label='reconst, offset {i}'.format(i=i)) # reconst[period//2:-period//2] = reconst[period//2:-period//2] - corrn/divisor reconst[period // 2:1 - period // 2] = reconst[period // 2:1 - period // 2] + smooth( err, period) / smooth(weight, period) # plot(smooth(err,period)/smooth(weight,period)) debug_(pyfusion.DEBUG, 1, key='restore_sin') if verbose > 0: ax1.plot(rd.timebase, reconst, 'r', label='reconst, final offset') ax1.legend() fig.show() return (reconst)
def find_shot_times(dev, shot, activity_indicator=None, debug=0): """ Note: This is inside a try/except - errors will just skip over!! fixme From the channel specified in the expression "activity_indicator", determine the beginning and end of pulse. A suitable expression is hard to find. For example, density usually persists too long after the shot, and sxrays appear a little late in the shot. The magnetics may be useful if magnet power supply noise could be removed. (had trouble with lhd 50628 until adj threshold ?start and end were at 0.5-0.6 secs ) >>> import pyfusion >>> sh=pyfusion.core.get_shot(15043,activity_indicator="MP4") >>> print('start=%.3g, end=%.3g' % (sh.pulse_start, sh.pulse_end) ) start=177, end=218 >>> sh=pyfusion.core.get_shot(33372,activity_indicator="MP4") >>> print('start=%.3g, end=%.3g' % (sh.pulse_start, sh.pulse_end) ) start=168, end=290 """ from pyfusion.data.signal_processing import smooth, smooth_n if debug>2: exception = None # allow all exceptions to crash to debug else: exception = Exception if activity_indicator=="": if pyfusion.OPT>5: print(str(' No activity indicator connected to shot %d, ' 'please consider implementing one to improve speed' % shot)) return((pyfusion.settings.SHOT_T_MIN, pyfusion.settings.SHOT_T_MAX)) diff_method = False try: # if a single channel ch = dev.acq.getdata(shot, activity_indicator) except exception: if pyfusion.VERBOSE>0: print("using default activity indicator") if dev.name == 'HeliotronJ': diff_method = True; cha = "HeliotronJ_MP3" chb = "HeliotronJ_MP1" elif dev.name == 'LHD': diff_method = True; cha = "MP4" chb = "MP6" ## Assume the start baseline and the end baselines are different (e.g. MICRO01!) # for now, we hardwire in activity in MP1 # later, change this to something like 'rms(pyf_hpn("MP1",2e3,4))>0.1' # note: 15043 is a tricky test (bump at 290) (3v, 5us spike) threshold_type = True; level_type = False # the differential method should be useful for all, # but relies on the relative sensititivy of two channels to mains ripple # so only implement selectively. if not diff_method: sig = ch[activity_indicator] timebase = ch.timebase else: activity_indicator = 'diff('+cha+ '-' +chb + ')' if level_type: n_avg = 10 n_smooth = n_avg csum = cumsum(sig) # just the valid bit - signal_processing.smooth() does this better. sm_sig = (csum[2*n_smooth:] - csum[n_smooth:-n_smooth])/n_smooth maxpp = max(sm_sig)-min(sm_sig) threshold = max(0.005, maxpp/20) elif threshold_type: n_avg = 300 # ripple is about 3ms (need to make this in phys units) n_smooth = n_avg if diff_method: # subtract two distant probes with similar power supply pickup. # distant increases phase diff hence real signal, and PS pickup will reduce if similar levels. ch1 = dev.acq.getdata(shot, cha) siga=ch1[cha] timebase = ch1.timebase ch2 = dev.acq.getdata(shot, chb) sigb=ch2[chb] tb2 = ch2.timebase if np.max(np.abs(tb2[0:10] - timebase[0:10]))> 1e-6: raise LookupError('timebases of {ca} and {cb} are different: ' '\n {tb1} \n{tb2}' .format(ca=cha, cb=chb, tb1=timebase[0:10], tb2=tb2[0:10])) if pyfusion.VERBOSE>2: print("find_shot_times diff method, ids = %d, %d" % (id(siga), id(sigb))) sig = siga-sigb sm_sig=sqrt(smooth((sig-smooth(sig,n_smooth,keep=1))**2,n_smooth,keep=1)) sm_sig[-n_smooth:]=sm_sig[-2*n_smooth:-n_smooth] tim=timebase threshold = 0.03 # good compromise is 0.02, 200 points, 1st order else: (inds, LP_sig) = smooth_n(sig,n_smooth,iter=4, indices=True, timebase=timebase) HP_sig = sig[inds] - LP_sig (tim,sm_sigsq) = smooth_n(HP_sig*HP_sig,n_smooth, timebase=timebase[inds]) sm_sig = sqrt(sm_sigsq) threshold = 0.02 # good compromise is 0.02, 1500 points, 4th order maxpp = max(sm_sig)-min(sm_sig) start_bl = average(sm_sig[0:n_avg]) end_bl = average(sm_sig[-n_avg:]) # if signal is quiet, but shows a contrast > 5, reduce threshold if maxpp < .1 and ((start_bl < maxpp/5) or (end_bl < maxpp/5)): threshold = maxpp/3 # first_inds = (abs(sm_sig-start_bl) > threshold).nonzero()[0] # last_inds = (abs(sm_sig-end_bl) > threshold).nonzero()[0] # New code is impulse proof - feature needs to last longer than one interval n_smooth first_inds=(smooth(abs(sm_sig-start_bl) > threshold, 2*n_smooth)>0.7).nonzero()[0] last_inds=(smooth(abs(sm_sig-end_bl) > threshold, 2*n_smooth)>0.7).nonzero()[0] if (debug>0) or pyfusion.VERBOSE>2: fmt="%d: %s, threshold = %.3g, n_smooth=%d,"+\ "n_avg=%d " fmt2="maxpp= %.3g, start_baseline=%.3g, end_baseline=%.3g,"+\ " threshold=%.3g" info1=str(fmt % (shot, activity_indicator, threshold, n_smooth, n_avg)) info2=str(fmt2 % (maxpp, start_bl, end_bl, threshold)) print("activity indicator " + info1+'\n'+info2) if (debug>0) or (pyfusion.VERBOSE>2): # plot before possible error signalled pl.plot(timebase, sig, 'c') pl.plot(tim, sm_sig,'b') pl.title('smoothed and raw signals used in finding active time of shot') pl.xlabel(info1+'\n'+info2) xr=pl.xlim() pl.plot([xr[0],mean(xr)], array([1,1])*start_bl) pl.plot([mean(xr),xr[1]], array([1,1])*end_bl) if len(first_inds) ==0 or len(last_inds) ==0: raise ValueError( 'could not threshold the activity channel %s, %d start, %d end inds ' % (activity_indicator, len(first_inds), len(last_inds))) ## the first n_smooth is a correction for the lass of data in smoothing ## the last is a margin of error ## (have!) should replace this with actual corresponding time #start_time=ch.timebase[max(0,min(first_inds)+n_smooth-n_smooth)] #end_time=ch.timebase[min(len(sig)-1,max(last_inds)+ # n_smooth+n_smooth)] start_time = tim[min(first_inds)] end_time = tim[max(last_inds)] end_time = min(end_time,timebase[-1]) if pyfusion.VERBOSE>2: print(end_time, last_inds) if (debug>0) or pyfusion.VERBOSE>4: # two crosses mark the endpoints pl.plot([start_time,end_time],[start_bl, end_bl], " +k", markersize=20, mew=0.5) pl.plot([start_time,end_time],[start_bl, end_bl], " ok", mfc='None', markersize=20, mew=1.5) # scatter is "out of date" - integer width, different conventions and is hidden # underneath plots # pl.scatter([start_time,end_time],[start_bl, end_bl], s=100, marker="+", linewidth=2) if pyfusion.VERBOSE>0: print("found start time on %d of %.5g, end = %.5g using %s" % (shot, start_time, end_time, activity_indicator)) return(start_time, end_time)
dev = pyfusion.getDevice(dev_name) try: dat = dev.acq.getdata(shot, diag_name) except: bads.append(shot) continue if dat.signal[0] > -10 or dat.signal[-1] > -30: IPs.append(shot) continue datr = dat.reduce_time([bl[0], bl[3]], copy=True) # first baseline removal to get rise and fall points datflat = datr.remove_baseline(baseline=bl) tmp_peak = np.max( smooth(datflat.signal, n_smooth=.02, timebase=datflat.timebase)[1]) (filt_tb, filtd) = smooth(datflat.signal, n_smooth=tfilt, timebase=datflat.timebase, causal=0) # find a level where there is only two intersections for div in range(100, 1, -1): whigh = np.where(filtd > tmp_peak / div)[0] if (len(np.unique(np.diff(whigh))) == 1) and len(whigh) > len(filtd) // 10: method = 'best' break else: method = 'simple' if debug > 1: raise ValueError("can't find the pulse")
def find_shot_times(dev, shot, activity_indicator=None, debug=0): """ Note: This is inside a try/except - errors will just skip over!! fixme From the channel specified in the expression "activity_indicator", determine the beginning and end of pulse. A suitable expression is hard to find. For example, density usually persists too long after the shot, and sxrays appear a little late in the shot. The magnetics may be useful if magnet power supply noise could be removed. (had trouble with lhd 50628 until adj threshold ?start and end were at 0.5-0.6 secs ) >>> import pyfusion >>> sh=pyfusion.core.get_shot(15043,activity_indicator="MP4") >>> print('start=%.3g, end=%.3g' % (sh.pulse_start, sh.pulse_end) ) start=177, end=218 >>> sh=pyfusion.core.get_shot(33372,activity_indicator="MP4") >>> print('start=%.3g, end=%.3g' % (sh.pulse_start, sh.pulse_end) ) start=168, end=290 """ from pyfusion.data.signal_processing import smooth, smooth_n if debug > 2: exception = None # allow all exceptions to crash to debug else: exception = Exception if activity_indicator == "": if pyfusion.OPT > 5: print( str(' No activity indicator connected to shot %d, ' 'please consider implementing one to improve speed' % shot)) return ((pyfusion.settings.SHOT_T_MIN, pyfusion.settings.SHOT_T_MAX)) diff_method = False try: # if a single channel ch = dev.acq.getdata(shot, activity_indicator) except exception: if pyfusion.VERBOSE > 0: print("using default activity indicator") if dev.name == 'HeliotronJ': diff_method = True cha = "HeliotronJ_MP3" chb = "HeliotronJ_MP1" elif dev.name == 'LHD': diff_method = True cha = "MP4" chb = "MP6" ## Assume the start baseline and the end baselines are different (e.g. MICRO01!) # for now, we hardwire in activity in MP1 # later, change this to something like 'rms(pyf_hpn("MP1",2e3,4))>0.1' # note: 15043 is a tricky test (bump at 290) (3v, 5us spike) threshold_type = True level_type = False # the differential method should be useful for all, # but relies on the relative sensititivy of two channels to mains ripple # so only implement selectively. if not diff_method: sig = ch[activity_indicator] timebase = ch.timebase else: activity_indicator = 'diff(' + cha + '-' + chb + ')' if level_type: n_avg = 10 n_smooth = n_avg csum = cumsum(sig) # just the valid bit - signal_processing.smooth() does this better. sm_sig = (csum[2 * n_smooth:] - csum[n_smooth:-n_smooth]) / n_smooth maxpp = max(sm_sig) - min(sm_sig) threshold = max(0.005, maxpp / 20) elif threshold_type: n_avg = 300 # ripple is about 3ms (need to make this in phys units) n_smooth = n_avg if diff_method: # subtract two distant probes with similar power supply pickup. # distant increases phase diff hence real signal, and PS pickup will reduce if similar levels. ch1 = dev.acq.getdata(shot, cha) siga = ch1[cha] timebase = ch1.timebase ch2 = dev.acq.getdata(shot, chb) sigb = ch2[chb] tb2 = ch2.timebase if np.max(np.abs(tb2[0:10] - timebase[0:10])) > 1e-6: raise LookupError('timebases of {ca} and {cb} are different: ' '\n {tb1} \n{tb2}'.format( ca=cha, cb=chb, tb1=timebase[0:10], tb2=tb2[0:10])) if pyfusion.VERBOSE > 2: print("find_shot_times diff method, ids = %d, %d" % (id(siga), id(sigb))) sig = siga - sigb sm_sig = sqrt( smooth((sig - smooth(sig, n_smooth, keep=1))**2, n_smooth, keep=1)) sm_sig[-n_smooth:] = sm_sig[-2 * n_smooth:-n_smooth] tim = timebase threshold = 0.03 # good compromise is 0.02, 200 points, 1st order else: (inds, LP_sig) = smooth_n(sig, n_smooth, iter=4, indices=True, timebase=timebase) HP_sig = sig[inds] - LP_sig (tim, sm_sigsq) = smooth_n(HP_sig * HP_sig, n_smooth, timebase=timebase[inds]) sm_sig = sqrt(sm_sigsq) threshold = 0.02 # good compromise is 0.02, 1500 points, 4th order maxpp = max(sm_sig) - min(sm_sig) start_bl = average(sm_sig[0:n_avg]) end_bl = average(sm_sig[-n_avg:]) # if signal is quiet, but shows a contrast > 5, reduce threshold if maxpp < .1 and ((start_bl < maxpp / 5) or (end_bl < maxpp / 5)): threshold = maxpp / 3 # first_inds = (abs(sm_sig-start_bl) > threshold).nonzero()[0] # last_inds = (abs(sm_sig-end_bl) > threshold).nonzero()[0] # New code is impulse proof - feature needs to last longer than one interval n_smooth first_inds = (smooth(abs(sm_sig - start_bl) > threshold, 2 * n_smooth) > 0.7).nonzero()[0] last_inds = (smooth(abs(sm_sig - end_bl) > threshold, 2 * n_smooth) > 0.7).nonzero()[0] if (debug > 0) or pyfusion.VERBOSE > 2: fmt="%d: %s, threshold = %.3g, n_smooth=%d,"+\ "n_avg=%d " fmt2="maxpp= %.3g, start_baseline=%.3g, end_baseline=%.3g,"+\ " threshold=%.3g" info1 = str(fmt % (shot, activity_indicator, threshold, n_smooth, n_avg)) info2 = str(fmt2 % (maxpp, start_bl, end_bl, threshold)) print("activity indicator " + info1 + '\n' + info2) if (debug > 0) or (pyfusion.VERBOSE > 2): # plot before possible error signalled pl.plot(timebase, sig, 'c') pl.plot(tim, sm_sig, 'b') pl.title( 'smoothed and raw signals used in finding active time of shot') pl.xlabel(info1 + '\n' + info2) xr = pl.xlim() pl.plot([xr[0], mean(xr)], array([1, 1]) * start_bl) pl.plot([mean(xr), xr[1]], array([1, 1]) * end_bl) if len(first_inds) == 0 or len(last_inds) == 0: raise ValueError( 'could not threshold the activity channel %s, %d start, %d end inds ' % (activity_indicator, len(first_inds), len(last_inds))) ## the first n_smooth is a correction for the lass of data in smoothing ## the last is a margin of error ## (have!) should replace this with actual corresponding time #start_time=ch.timebase[max(0,min(first_inds)+n_smooth-n_smooth)] #end_time=ch.timebase[min(len(sig)-1,max(last_inds)+ # n_smooth+n_smooth)] start_time = tim[min(first_inds)] end_time = tim[max(last_inds)] end_time = min(end_time, timebase[-1]) if pyfusion.VERBOSE > 2: print(end_time, last_inds) if (debug > 0) or pyfusion.VERBOSE > 4: # two crosses mark the endpoints pl.plot([start_time, end_time], [start_bl, end_bl], " +k", markersize=20, mew=0.5) pl.plot([start_time, end_time], [start_bl, end_bl], " ok", mfc='None', markersize=20, mew=1.5) # scatter is "out of date" - integer width, different conventions and is hidden # underneath plots # pl.scatter([start_time,end_time],[start_bl, end_bl], s=100, marker="+", linewidth=2) if pyfusion.VERBOSE > 0: print("found start time on %d of %.5g, end = %.5g using %s" % (shot, start_time, end_time, activity_indicator)) return (start_time, end_time)
wc = np.where(rd.signal[0] < clip_level_minus)[0] weight = 1 + 0*reconst weight[wc] = 0 period = 1000 from pyfusion.data.signal_processing import smooth # iterate to make waves match where there is no clipping for i in range(6): err = rd.signal[0] - reconst err[wc] = 0 print(sum(err)/sum(weight)) corrn = np.cumsum(err[0:-period]) - np.cumsum(err[period:]) divisor = np.cumsum(weight[0:-period]) - np.cumsum(weight[period:]) wnef = np.where(divisor <= 100)[0] divisor[wnef] = 100 if verbose>1: ax1.plot(rd.timebase, reconst, '--', label='reconst, offset {i}'.format(i=i)) # reconst[period//2:-period//2] = reconst[period//2:-period//2] - corrn/divisor reconst[period//2:1-period//2] = reconst[period//2:1-period//2] + smooth(err,period)/smooth(weight,period) # plot(smooth(err,period)/smooth(weight,period)) debug_(pyfusion.DEBUG, 1, key='restore_sin') if method==2: if verbose>0: ax1.plot(rd.timebase, reconst,'r', label='reconst, final offset') ax1.legend() fig.show()