;+ ;PROCEDURE: ; mvn_swe_sc_pot ; ;PURPOSE: ; Estimates the spacecraft potential from SWEA energy spectra. The basic ; idea is to look for a break in the energy spectrum (sharp change in flux ; level and slope). No attempt is made to estimate the potential when the ; spacecraft is in darkness (expect negative potential) or below 250 km ; altitude (expect small or negative potential). ; ;AUTHOR: ; David L. Mitchell ; ;CALLING SEQUENCE: ; mvn_swe_sc_pot, potential=dat ; ;INPUTS: ; none - energy spectra are obtained from SWEA common block. ; ;KEYWORDS: ; POTENTIAL: Returns a time-ordered array of spacecraft potentials ; ; ERANGE: Energy range over which to search for the potential. ; Default = [3.,30.] ; ; THRESH: Threshold for the minimum slope: d(logF)/d(logE). ; Default = 0.05 ; ; A smaller value includes more data and extends the range ; over which you can estimate the potential, but at the ; expense of making more errors. ; ; MINFLUX: Minimum peak energy flux. Default = 1e6. ; ; DEMAX: The largest allowable energy width of the spacecraft ; potential feature. This excludes features not related ; to the spacecraft potential at higher energies (often ; observed downstream of the shock). Default = 6 eV. ; ; FUDGE: Multiply the derived potential by this fudge factor. ; (for calibration against LPW). Default = 1. ; ; DDD: Use 3D data to calculate potential. Allows bin masking, ; but lower cadence and typically lower energy resolution. ; ; ABINS: When using 3D spectra, specify which anode bins to ; include in the analysis: 0 = no, 1 = yes. ; Default = replicate(1,16) ; ; DBINS: When using 3D spectra, specify which deflection bins to ; include in the analysis: 0 = no, 1 = yes. ; Default = replicate(1,6) ; ; OBINS: When using 3D spectra, specify which solid angle bins to ; include in the analysis: 0 = no, 1 = yes. ; Default = reform(ABINS#DBINS,96). Takes precedence over ; ABINS and OBINS. ; ; MASK_SC: Mask the spacecraft blockage. This is in addition to any ; masking specified by the above three keywords. ; Default = 1 (yes). ; ; PANS: Named varible to hold the tplot panels created. ; ; OVERLAY: Overlay the result on the energy spectrogram. ; ; SETVAL: Make no attempt to estimate the potential, just set it to ; this value. Units = volts. No default. ; ; BADVAL: If the algorithm cannot estimate the potential, then set it ; to this value. Units = volts. Default = NaN. ; ; ANGCORR: Angular distribution correction based on interpolated 3d data ; to emphasize the returning photoelectrons and improve ; the edge detection (added by Yuki Harada). ; ; NEGPOT: Calculate negative potentials with mvn_swe_sc_negpot. ; Default = 1 (yes). ; ; STA_POT: Use STATIC-derived potentials to fill in gaps. This is ; especially useful in the high-altitude shadow region. ; Assumes that you have calculated STATIC potentials. ; (See mvn_sta_scpot_load.pro) ; ; LPW_POT: Use pre-calculated SWEA+LPW-derived potentials. There is ; a ~2-week delay in the production of this dataset. You can ; set this keyword to the full path and filename of a tplot ; save/restore file, if one exists. Otherwise, this routine ; will determine the potential from SWEA alone. ; ; POT_IN_SHDW: Calculate negative potentials with 'mvn_swe_sc_negpot_twodir_burst', ; Default = 0 (no). This routine calculates He II in both field-aligned ; directions, and uses the less negative one as s/c potentials if detected ; in both directions. The results are filled to SWEA common block as well. ; Right row, it requires keyword "NEGPOT" to be 1, which is default. ; ;OUTPUTS: ; None - Result is stored in SPEC data structure, returned via POTENTIAL ; keyword, and stored as a TPLOT variable. ; ; $LastChangedBy: dmitchell $ ; $LastChangedDate: 2017-05-08 17:26:51 -0700 (Mon, 08 May 2017) $ ; $LastChangedRevision: 23278 $ ; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/tags/spedas_2_00/projects/maven/swea/mvn_swe_sc_pot.pro $ ; ;- pro mvn_swe_sc_pot, potential=potential, erange=erange2, fudge=fudge, thresh=thresh2, dEmax=dEmax2, $ pans=pans, overlay=overlay, ddd=ddd, abins=abins, dbins=dbins, obins=obins, $ mask_sc=mask_sc, setval=setval, badval=badval, angcorr=angcorr, minflux=minflux2, $ negpot=negpot, sta_pot=sta_pot, lpw_pot=lpw_pot, pot_in_shdw=pot_in_shdw compile_opt idl2 @mvn_swe_com common swe_pot_com, Espan, thresh, dEmax, minflux if (size(Espan,/type) eq 0) then begin Espan = [3.,30.] thresh = 0.05 dEmax = 6. minflux = 1.e6 endif if (n_elements(erange2) gt 1) then Espan = float(minmax(erange2)) if (size(thresh2,/type) gt 0) then thresh = float(thresh2) if (size(dEmax2,/type) gt 0) then dEmax = float(dEmax2) if (size(minflux2,/type) gt 0) then minflux = float(minflux2) if (size(mvn_swe_engy,/type) ne 8) then begin print,"No energy data loaded. Use mvn_swe_load_l0 first." phi = 0 return endif if (size(setval,/type) ne 0) then begin print,"Setting the s/c potential to: ",setval mvn_swe_engy.sc_pot = setval swe_sc_pot = replicate(swe_pot_struct, n_elements(mvn_swe_engy)) swe_sc_pot[*].time = mvn_swe_engy.time swe_sc_pot[*].potential = setval pot = {x:mvn_swe_engy.time, y:mvn_swe_engy.sc_pot} store_data,'mvn_swe_sc_pot',data=pot if keyword_set(overlay) then begin str_element,pot,'thick',2,/add str_element,pot,'color',0,/add str_element,pot,'psym',3,/add store_data,'swe_pot_overlay',data=pot store_data,'swe_a4_pot',data=['swe_a4','swe_pot_overlay'] ylim,'swe_a4_pot',3,5000,1 endif return endif if (size(badval,/type) eq 0) then badval = !values.f_nan else badval = float(badval) if (size(negpot,/type) eq 0) then negpot = 1 ;if (size(pot_in_shdw,/type) eq 0) then pot_in_shdw = 1 ; Clear any previous potential calculations mvn_swe_engy.sc_pot = badval ; Get pre-calculated potentials from combined SWEA-LPW analysis ... ok = 0 if keyword_set(lpw_pot) then begin if (size(lpw_pot,/type) eq 7) then tplot_restore, file=lpw_pot $ else mvn_swe_lpw_scpot_restore get_data,'mvn_swe_lpw_scpot_pow',data=lpwpot,index=i if (i gt 0) then begin t = mvn_swe_engy.time npts = n_elements(t) phi = replicate(badval, npts) phi = interpol(lpwpot.y, lpwpot.x, t) mvn_swe_engy.sc_pot = phi ok = 1 endif endif ; ... otherwise calculate potential from SWEA alone if (not ok) then begin Espan = minmax(float(Espan)) if not keyword_set(fudge) then fudge = 1. if keyword_set(ddd) then dflg = 1 else dflg = 0 if (n_elements(abins) ne 16) then abins = replicate(1B, 16) if (n_elements(dbins) ne 6) then dbins = replicate(1B, 6) if (n_elements(obins) ne 96) then begin obins = replicate(1B, 96, 2) obins[*,0] = reform(abins # dbins, 96) obins[*,1] = obins[*,0] endif else obins = byte(obins # [1B,1B]) if (size(mask_sc,/type) eq 0) then mask_sc = 1 if keyword_set(mask_sc) then obins = swe_sc_mask * obins if (dflg) then begin ok = 0 if (size(mvn_swe_3d,/type) eq 8) then begin t = mvn_swe_3d.time npts = n_elements(t) e = fltarr(64,npts) f = e ok = 1 endif if ((not ok) and size(swe_3d,/type) eq 8) then begin t = swe_3d.time npts = n_elements(t) e = fltarr(64,npts) f = e ok = 1 endif if (not ok) then begin print, "No valid 3D data." return endif for i=0L,(npts-1L) do begin ddd = mvn_swe_get3d(t[i], units='eflux') if (ddd.time gt t_mtx[2]) then boom = 1 else boom = 0 ondx = where(obins[*,boom] eq 1B, ocnt) onorm = float(ocnt) obins_b = replicate(1B, 64) # obins[*,boom] e[*,i] = ddd.energy[*,0] f[*,i] = total(ddd.data * obins_b, 2, /nan)/onorm endfor endif else begin old_units = mvn_swe_engy[0].units_name mvn_swe_convert_units, mvn_swe_engy, 'eflux' t = mvn_swe_engy.time npts = n_elements(t) e = mvn_swe_engy.energy f = mvn_swe_engy.data endelse ; Angular distribution correction based on interpolated 3d data ; to emphasize the returning photoelectrons. ; This section was added by Yuki Harada. if keyword_set(angcorr) and (size(mvn_swe_3d,/type) eq 8) then begin ww = finite(mvn_swe_3d.data) * 1. wsky = where( mvn_swe_3d.phi gt 112.5 and mvn_swe_3d.phi lt 292.5 $ and mvn_swe_3d.theta gt -45 and mvn_swe_3d.theta lt 45 , comp=cwsky ) ww[cwsky] = 0. skyflux = total(mvn_swe_3d.data*mvn_swe_3d.domega*ww,2,/nan) $ /total(mvn_swe_3d.domega*ww,2,/nan) ww = finite(mvn_swe_3d.data) * 1. aveflux = total(mvn_swe_3d.data*mvn_swe_3d.domega*ww,2,/nan) $ /total(mvn_swe_3d.domega*ww,2,/nan) fr = f * !values.f_nan for j=0,63 do fr[j,*] = interp(reform(skyflux[j,*]/aveflux[j,*]),mvn_swe_3d.time,t) < 1.2 ; A maximum factor of 1.2 is set to avoid too much emphasis on lowest ; energy photoelectrons f = f * fr endif indx = where(e[*,0] lt 60., n_e) e = e[indx,*] f = alog10(f[indx,*]) potstr = {time : 0D , $ pot : !values.f_nan , $ dE : !values.f_nan , $ amp : !values.f_nan , $ flg : 0 } potential = replicate(potstr, npts) potential.time = t ; Filter out bad spectra potential.flg = 1 n_f = round(total(finite(f),1)) gndx = where(n_f eq n_e, ngud, complement=bad, ncomplement=nbad) if (ngud eq 0L) then begin print,"No good spectra!" return endif if (nbad gt 0L) then begin f[*,bad] = !values.f_nan potential[bad].flg = 0 endif ; Take first and second derivatives of log(eflux) w.r.t. log(E) df = f d2f = f for i=0L,(npts-1L) do df[*,i] = deriv(f[*,i]) for i=0L,(npts-1L) do d2f[*,i] = deriv(df[*,i]) ; Oversample and smooth n_es = 4*n_e emax = max(e, dim=1, min=emin) dloge = (alog10(emax) - alog10(emin))/float(n_es - 1) ee = 10.^((replicate(1.,n_es) # alog10(emax)) - (findgen(n_es) # dloge)) dfs = fltarr(n_es,npts) for i=0L,(npts-1L) do dfs[*,i] = interpol(df[*,i],n_es) d2fs = fltarr(n_es,npts) for i=0L,(npts-1L) do d2fs[*,i] = interpol(d2f[*,i],n_es) ; Trim to the desired energy search range indx = where((ee[*,0] gt Espan[0]) and (ee[*,0] lt Espan[1]), n_e) ee = ee[indx,*] dfs = dfs[indx,*] d2fs = d2fs[indx,*] ; The spacecraft potential is taken to be the maximum slope (dlogF/dlogE) ; within the search window. A fudge factor is included to adjust the estimate ; for cross calibration with LPW. ; ; Use diagnostics keywords in swe_engy_snap to plot these functions, together ; with the retrieved potential. zcross = d2fs*shift(d2fs,1,0) zcross[0,*] = 1. phi = replicate(badval, npts) for i=0L,(npts-1L) do begin indx = where((dfs[*,i] gt thresh) and (zcross[*,i] lt 0.), ncross) ; local maxima in slope if (ncross gt 0) then begin k = max(indx) ; lowest energy feature above threshold dfsmax = dfs[k,i] dfsmin = dfsmax/2. ; FWHM of slope while ((dfs[k,i] gt dfsmin) and (k lt n_e-1)) do k++ kmax = k k = max(indx) while ((dfs[k,i] gt dfsmin) and (k gt 0)) do k-- kmin = k dE = ee[kmin,i] - ee[kmax,i] if ((kmax eq (n_e-1)) or (kmin eq 0)) then dE = 2.*dEmax if (dE lt dEmax) then phi[i] = ee[max(indx),i] ; only accept narrow features potential[i].dE = dE potential[i].amp = dfsmax endif endfor ; Filter for low flux fmax = max(mvn_swe_engy.data, dim=1) indx = where(fmax lt minflux, count) if (count gt 0L) then begin phi[indx] = badval potential[indx].flg = -1 endif ; Filter out shadow regions get_data, 'wake', data=wake, index=i if (i eq 0) then begin maven_orbit_tplot, /current, /loadonly get_data, 'wake', data=wake, index=i endif if (i gt 0) then begin shadow = interpol(float(finite(wake.y)), wake.x, mvn_swe_engy.time) indx = where(shadow gt 0., count) if (count gt 0L) then begin phi[indx] = badval potential[indx].flg = -2 endif endif ; Filter out altitudes below 250 km get_data, 'alt', data=alt, index=i if (i eq 0) then begin maven_orbit_tplot, /current, /loadonly get_data, 'alt', data=alt, index=i endif if (i gt 0) then begin altitude = interpol(alt.y, alt.x, mvn_swe_engy.time) indx = where(altitude lt 250., count) if (count gt 0L) then begin phi[indx] = badval potential[indx].flg = -3 endif endif ; Apply fudge factor phi = phi*fudge potential.pot = phi if (not dflg) then begin mvn_swe_engy.sc_pot = phi mvn_swe_convert_units, mvn_swe_engy, old_units endif else begin mvn_swe_engy.sc_pot = interpol(phi,t,mvn_swe_engy.time) endelse ; Make tplot variables store_data,'df',data={x:t, y:transpose(dfs), v:transpose(ee)} options,'df','spec',1 ylim,'df',0,30,0 zlim,'df',0,0,0 store_data,'d2f',data={x:t, y:transpose(d2fs), v:transpose(ee)} options,'d2f','spec',1 ylim,'d2f',0,30,0 zlim,'d2f',0,0,0 store_data,'Potential',data=['d2f','mvn_swe_sc_pot'] ylim,'Potential',0,30,0 endif ; Store the result in the SWEA common block swe_sc_pot = replicate(swe_pot_struct, npts) swe_sc_pot.time = t swe_sc_pot.potential = phi swe_sc_pot.valid = 1 pot = {x:t, y:phi} store_data,'mvn_swe_sc_pot',data=pot pans = 'mvn_swe_sc_pot' ; Estimate negative potentials if keyword_set(negpot) then begin if keyword_set(pot_in_shdw) then mvn_swe_sc_negpot_twodir_burst,/fill,/shadow mvn_swe_sc_negpot, /fill indx = where(swe_sc_pot.potential lt 0., count) if (count gt 0L) then phi[indx] = swe_sc_pot[indx].potential pot_pan = 'mvn_swe_pot_all' store_data,'swe_pot_lab',data={x:minmax(t), y:replicate(!values.f_nan,2,2)} options,'swe_pot_lab','labels',['swe-','swe+'] options,'swe_pot_lab','colors',[6,!p.color] options,'swe_pot_lab','labflag',1 store_data,pot_pan,data=['swe_pot_lab','mvn_swe_sc_pot','neg_pot','pot_inshdw'] options,'neg_pot','constant',!values.f_nan options,'neg_pot','color',6 options,'pot_inshdw','constant',!values.f_nan options,'pot_inshdw','color',1 options,pot_pan,'ytitle','S/C Potential!cVolts' options,pot_pan,'constant',[-1,3] endif ; Incorporate STATIC-derived potential. Only used to fill in times when ; SWEA/LPW potential is unavailable. if keyword_set(sta_pot) then begin get_data,'mvn_sta_c6_scpot',data=stapot,index=i if (i gt 0) then begin indx = where(stapot.y ge 0., count) if (count gt 0L) then stapot.y[indx] = badval nndx = nn(stapot.x, t) if (finite(badval)) then indx = where(phi eq badval, count) $ else indx = where(~finite(phi), count) if (count gt 0L) then begin phi[indx] = stapot.y[nndx[indx]] swe_sc_pot.potential = phi mvn_swe_engy.sc_pot = phi sphi = replicate(!values.f_nan, n_elements(phi)) sphi[indx] = phi[indx] store_data,'sta_pot',data={x:t, y:sphi} options,'sta_pot','color',4 get_data,'mvn_swe_pot_all',data=tpot,index=i if (i gt 0) then begin tpot = [tpot,'sta_pot'] store_data,'swe_pot_lab',data={x:minmax(t), y:replicate(!values.f_nan,2,3)} options,'swe_pot_lab','labels',['sta','swe-','swe+'] options,'swe_pot_lab','colors',[4,6,!p.color] options,'swe_pot_lab','labflag',1 endif else begin tpot = ['mvn_swe_sc_pot','sta_pot'] pot_pan = 'mvn_swe_pot_all' store_data,'swe_pot_lab',data={x:minmax(t), y:replicate(!values.f_nan,2,2)} options,'swe_pot_lab','labels',['sta','swe+'] options,'swe_pot_lab','colors',[4,!p.color] options,'swe_pot_lab','labflag',1 endelse store_data,'mvn_swe_pot_all',data=tpot endif endif else print,"Can't find tplot variable: mvn_sta_c6_scpot" endif if keyword_set(overlay) then begin str_element,pot,'thick',2,/add str_element,pot,'color',0,/add str_element,pot,'psym',3,/add store_data,'swe_pot_overlay',data=pot store_data,'swe_a4_pot',data=['swe_a4','swe_pot_overlay'] ylim,'swe_a4_pot',3,5000,1 tplot_options, get=opt str_element, opt, 'varnames', varnames, success=ok if (ok) then begin i = (where(varnames eq 'swe_a4'))[0] if (i ne -1) then begin varnames[i] = 'swe_a4_pot' tplot, varnames endif endif endif return end