Example #1
0
def local_energy_dispersion(spectra, n=1, order=2, correctE=True, \
       refname=None, ret_data=False, verbosity=0, **kwargs):
    """
    calculate the position-dependent energy dispersion from 
    the shift of the ZLP with a change in beam energy

    spectra  ... file containing the spectrum image (Nspectra, Npx)
    n        ... (opt) use ZLP shift between spectrum i and i+n
    order    ... (opt) order of the fitting polynomial for the distances
    correctE ... (opt) if True, beam energy is corrected using BeamEnergy
    refname  ... (opt) filename of reference spectrum for second peak, 
                        which will be used instead of the ZLP
    ret_data ... (opt) return (pos, diff)-points used for the fit
    verbosity... (opt) verbosity (0=silent, 2=plotting fit, 3=debug)

    RETURNS - function object returning the energy dispersion for any 
                position on the detector
            - if ret_data is True, data used to create this function object 

  """
    # set up beam energy correction
    Beam = BeamEnergy()

    data = []
    pos = []
    diff = []
    dE = []
    for filename in spectra:

        # determine zlp position for each beam energy
        if refname is not None:
            Eref, d, zl = get_peak_pos(filename,
                                       refname=refname,
                                       verbosity=verbosity - 1,
                                       **kwargs)
        else:
            Eref, zl = get_peak_pos(filename,
                                    verbosity=verbosity - 1,
                                    **kwargs)

        # correct BeamEnergy
        E = Beam.En(Eref) if correctE else Eref

        # remove NaN's in zl (fit failed) and E (no correction available)
        ind = ~np.isnan(zl[:, 0] + E)
        E = E[ind]
        zl = zl[ind]
        assert np.allclose(Eref[ind][1:] - Eref[ind][:-1], Eref[1] - Eref[0])
        # only even spacing in ZLP energy shifts allowed so far

        # calculate energy dispersion from difference in adjacent spectra
        data.append([Eref[ind], zl])
        # zl[:,0]=mean pos
        pos.append((zl[n:, 0] + zl[:-n, 0]) / 2)
        # average position on detector
        diff.append(-(zl[n:, 0] - zl[:-n, 0]))
        # distance between ZLP i and i+n
        dE.append(E[n:] - E[:-n])
        # corresponding energy distance

        # TODO: allow for many spectra with uneven energy spacing
        dEref = Eref[n] - Eref[0]
        # nominal energy difference

    # plotting
    if verbosity > 1:
        fig = plt.figure()
        ax = plt.gca()
        plt.title("Position-dependent peak distance"+\
                  " (n=%d, $\Delta E_{ref}$=%4.1f eV)"%(n,dEref))
        for i in range(len(pos)):
            ax.plot(pos[i],
                    diff[i],
                    'ko',
                    alpha=0.1,
                    label="uncorrected" if i == 0 else "")
            ax.plot(pos[i],
                    diff[i] * dEref / dE[i],
                    'o',
                    markersize=6,
                    label="%s" % spectra[i].split('/')[-1].split('.dm3')[0])

    # flatten lists (one level)
    pos = np.asarray(sum(map(list, pos), []))
    diff = np.asarray(sum(map(list, diff), []))
    dE = np.asarray(sum(map(list, dE), []))

    # fitting polynomial to the Energy-corrected appearent size of the scale
    ind = ~np.isnan(pos)
    # index for outliers
    diff_corr = diff * dEref / dE
    disp = np.poly1d(np.polyfit(pos[ind], diff_corr[ind], order))

    if verbosity > 1:
        plt.gca().plot(pos[~ind], diff[~ind], 'rx', label="outliers")
        plt.gca().plot(disp(np.arange(4096)), 'k-', label="fit")
        ax.set_xlabel("position on detector [px]")
        if correctE:
            ax.set_ylabel(
                "corrected ZLP distance  $ \Delta x * (\Delta E_{ref} / \Delta E)$ [px]"
            )
        else:
            ax.set_ylabel("ZLP distance dx [px]")
        #ax.set_ylim(460,475);
        ax.legend(loc=0)

    # calculate energy dispersion dE/dy
    e2x = trafo.NonlinearDispersion(disp, scale=dEref, xrange=(0, 4096))
    #

    # DEBUG: test energy calibration for positions of ZLP:
    if verbosity > 2:
        fig = plt.figure()
        ax = fig.add_subplot(111)
        # reference curve for beam energy
        Beam = BeamEnergy()
        ax.plot(Beam.E, Beam.E_corr, 'k-', label='reference')

        # beam energy from ZLP position and energy dispersion
        for i, (Eref, zl) in enumerate(data):

            E, _ = e2x.inverse(zl[:, 0], zl[:, 0])
            dE = -(E - E[0])
            # shift of ZLP from calibrated E-axis
            E_corr = dE + Beam.En(Eref[0])
            # corrected Beam energy for each ZLP
            ax.plot(Eref, E_corr - Eref, 'x', label=spectra[i].split('/')[-1])
            ax.set_xlabel("nominal beam energy offset $E_0$ [eV]")
            ax.set_ylabel("estimated deviation $E - E_0$ [eV]")
            ax.set_ylim(-0.3, 0.3)
            ax.legend(loc=0)

    # output transformation
    if verbosity > 1:
        print "Transformation to linear energy axis e2x: \n", e2x.info(3)
    return e2x if not ret_data else (e2x, np.vstack(
        (pos[ind], diff_corr[ind])))
Example #2
0
def get_dispersion(spectra, refname, DE=None, order=2, \
                      ret_data=False, verbosity=0, **kwargs):
    """
    calculate the position-dependent energy dispersion from 
    the distance between two peaks (ZLP and plasmon reference)

    spectra  ... file containing the spectrum image (Nspectra, Npx)
    refname  ... filename of reference spectrum for second peak
    DE       ... (opt) use energy width of shifted scale to calibrate 
                 the absolute energy axis, otherwise we use the nominal
                 distance of two ZLPs
    order    ... (opt) order of the fitting polynomial for the distances
    ret_data ... (opt) return (pos, diff)-points used for the fit
    verbosity... (opt) verbosity (0=silent, 2=plotting fit, 3=debug)

    RETURNS function object returning the energy dispersion for any 
            position on the detector
  """

    pos = []
    diff = []
    data = []
    for filename in spectra:

        Eref, zl, pl = get_peak_pos(filename,
                                    refname,
                                    verbosity=verbosity,
                                    **kwargs)
        # order zl and pl (zl should be left peak)
        if np.nansum(pl[:, 0] - zl[:, 0]) < 0: zl, pl = pl, zl

        # remove NaN's in zl (fit failed) and BeamEnergy (no correction available)
        ind = ~np.isnan(zl[:, 0] + BeamEnergy().En(Eref))
        Eref = Eref[ind]
        zl = zl[ind]
        pl = pl[ind]

        # determine position-dependent peak distance
        data.append([Eref, zl, pl])
        # zl[:,0]=mean pos
        pos.append((zl[:, 0] + pl[:, 0]) / 2)
        # average position on detector
        diff.append(pl[:, 0] - zl[:, 0])
        # distance between peaks dy

    # plotting
    if verbosity > 1:
        fig = plt.figure()
        ax = plt.gca()
        plt.title("Position-dependent peak distance")
        for i in range(len(pos)):
            ax.plot(pos[i],
                    diff[i],
                    'x',
                    label="%s" % spectra[i].split('/')[-1].split('.dm3')[0])

    # flatten lists (one level)
    pos = np.asarray(sum(map(list, pos), []))
    diff = np.asarray(sum(map(list, diff), []))

    # fitting polynomial to the appearent size of the scale
    ind = ~np.isnan(pos)
    # index for outliers
    disp = np.poly1d(np.polyfit(pos[ind], diff[ind], order))

    if verbosity > 1:
        plt.gca().plot(pos[~ind], diff[~ind], 'rx', label="outliers")
        plt.gca().plot(disp(np.arange(4096)), 'k-', label="fit")
        ax.set_xlabel("position on detector [px]")
        ax.set_ylabel("distance between ZLP and plasmon [px]")
        #ax.set_ylim(460,475);
        ax.legend(loc=0)

    # Calculate energy width of scale from the linear shift of the ZLP
    # with (corrected) beam energy
    if DE is None:

        # for each data series with N shifted spectra, we calculate the
        # missing scaling factor DE from the expected shift of the ZLPs
        DE = []
        for Eref, zl, pl in data:  #  data[series][quantity][Nspectra]

            # expected energy shift for n-th ZLP compared to initial pos.
            Ecorr = -BeamEnergy().En(Eref)
            # energy scale inversed wrt. E-q map
            Y = Ecorr[0] - Ecorr[1:]
            # measured energy shift for scale DE=1
            e2x = NonlinearDispersion(disp, scale=1)
            en = e2x.inverse(zl[:, 0], zl[:, 0])[0]
            X = en[0] - en[1:]
            #print Ecorr, Eref, Y, zl[:,0], en;
            # calculate scaling factor DE which minimizes residuals X*DE-Y
            # min(|X*DE-Y|^2) <=> 2(X*DE-Y)*X = 0 <=> DE=X*Y/X*X
            DE.append(np.dot(X, Y) / np.dot(X, X))

        # ouput
        if verbosity > 0:
            print "-- estimating scale DE ---------------------------"
            if verbosity > 1:
                for i in range(len(spectra)):
                    print "   optimal Eplasmon for '%s':   %8.5f eV " \
                            % (spectra[i].split('/')[-1], DE[i])
            print "\n Average Eplasmon:  (%5.3f +- %5.3f) eV" \
                        % (np.mean(DE), np.std(DE))

    # calculate energy dispersion dE/dy
    e2x = NonlinearDispersion(disp, scale=np.mean(DE))

    # DEBUG: test energy calibration for positions of ZLP:
    if verbosity > 2:
        fig = plt.figure()
        ax = fig.add_subplot(111)
        # reference curve for beam energy
        Beam = BeamEnergy()
        ax.plot(Beam.E, Beam.E_corr, 'k-', label='reference')

        # beam energy from ZLP position and energy dispersion
        for i, (Eref, zl, pl) in enumerate(data):

            E, _ = e2x.inverse(zl[:, 0], zl[:, 0])
            dE = -(E - E[0])
            # shift of ZLP from calibrated E-axis
            E_corr = dE + Beam.En(Eref[0])
            # corrected Beam energy for each ZLP
            ax.plot(Eref, E_corr - Eref, 'x', label=spectra[i].split('/')[-1])
            ax.set_xlabel("nominal beam energy offset $E_0$ [eV]")
            ax.set_ylabel("estimated deviation $E - E_0$ [eV]")
            ax.set_ylim(-0.3, 0.3)
            ax.legend(loc=0)

    # output transformation
    if verbosity > 1:
        print "Transformation to linear energy axis e2x: \n", e2x.info(3)
    return e2x if not ret_data else (e2x, np.vstack((pos[ind], diff[ind])))
Example #3
0
from TEMareels.ecal.energy_dispersion import get_dispersion;
from TEMareels.ecal.fit_peak_pos import get_peak_pos;

# calculate dispersion
spectra  = ["../tests/Eseries%i.tif" %i for i in range(1,4)];
refname  = "../tests/Ereference.msa";
order    = 2;
DE       = None;  # energy width of shifted scale [eV]
e2x      = get_dispersion(spectra,refname,verbosity=0,order=order,ampl_cut=0.5);

# calculate energy of ZLP for each of the series
E_ref = []; E_zl = []; E_pl = [];
for i,filename in enumerate(spectra):

  E,zl,pl=get_peak_pos(filename, refname, border=100, \
              ampl_cut=0.1, verbosity=2);     # positions [px]
  ind=~np.isnan(pl[:,0]-zl[:,0]);             # identify NaN's
  E  = E[ind]; zl = zl[ind]; pl = pl[ind]; 
  zl = e2x.inverse( zl[:,0], zl[:,0] )[0];    # energies  [eV]
  pl = e2x.inverse( pl[:,0], pl[:,0] )[0];
  s0 = np.nanargmax(zl);                      # remove offset
  E_zl.append( E[s0]+zl[s0]-zl );
  plOffset = np.mean(pl-zl) \
           if DE is None else DE;
  E_pl.append( E[s0]+zl[s0]+plOffset-pl);
  E_ref.append( E );
  
  #sE = np.argmin(np.abs(E-0));               # align at Energy E0
  #print sE, E_zl[-1][sE];
  #E_zl[-1] += E[sE]-E_zl[-1][sE];
  #E_pl[-1] += E[sE]-E_pl[-1][sE];
def local_energy_dispersion(spectra, n=1, order=2, correctE=True, \
       refname=None, ret_data=False, verbosity=0, **kwargs):
  """
    calculate the position-dependent energy dispersion from 
    the shift of the ZLP with a change in beam energy

    spectra  ... file containing the spectrum image (Nspectra, Npx)
    n        ... (opt) use ZLP shift between spectrum i and i+n
    order    ... (opt) order of the fitting polynomial for the distances
    correctE ... (opt) if True, beam energy is corrected using BeamEnergy
    refname  ... (opt) filename of reference spectrum for second peak, 
                        which will be used instead of the ZLP
    ret_data ... (opt) return (pos, diff)-points used for the fit
    verbosity... (opt) verbosity (0=silent, 2=plotting fit, 3=debug)

    RETURNS - function object returning the energy dispersion for any 
                position on the detector
            - if ret_data is True, data used to create this function object 

  """
  # set up beam energy correction
  Beam = BeamEnergy();

  data = []; pos = []; diff = []; dE = []; 
  for filename in spectra:

    # determine zlp position for each beam energy
    if refname is not None:
      Eref,d,zl=get_peak_pos(filename,refname=refname, verbosity=verbosity-1, **kwargs);
    else:
      Eref,zl  = get_peak_pos( filename, verbosity=verbosity-1, **kwargs);

    # correct BeamEnergy
    E   = Beam.En(Eref) if correctE else Eref;

    # remove NaN's in zl (fit failed) and E (no correction available)
    ind =~np.isnan(zl[:,0] + E); 
    E   = E[ind]; zl=zl[ind]; 
    assert np.allclose( Eref[ind][1:] - Eref[ind][:-1], Eref[1]-Eref[0] )
                    # only even spacing in ZLP energy shifts allowed so far

    # calculate energy dispersion from difference in adjacent spectra
    data.append( [Eref[ind],  zl] );         # zl[:,0]=mean pos
    pos.append(  (zl[n:,0] + zl[:-n,0])/2 ); # average position on detector
    diff.append(-(zl[n:,0] - zl[:-n,0])   ); # distance between ZLP i and i+n 
    dE.append( E[n:] - E[:-n] );             # corresponding energy distance
  
  # TODO: allow for many spectra with uneven energy spacing
    dEref = Eref[n]-Eref[0];                 # nominal energy difference
    
  # plotting
  if verbosity > 1:
    fig=plt.figure(); ax=plt.gca();
    plt.title("Position-dependent peak distance"+\
              " (n=%d, $\Delta E_{ref}$=%4.1f eV)"%(n,dEref));
    for i in range(len(pos)):
      ax.plot(pos[i], diff[i], 'ko', alpha=0.1, label="uncorrected" if i==0 else "");
      ax.plot(pos[i], diff[i]*dEref/dE[i], 'o', markersize=6, 
                            label="%s"%spectra[i].split('/')[-1].split('.dm3')[0]);
 
  # flatten lists (one level)
  pos  = np.asarray(sum(map(list,pos ),[])); 
  diff = np.asarray(sum(map(list,diff),[]));
  dE   = np.asarray(sum(map(list,dE),[]));

  # fitting polynomial to the Energy-corrected appearent size of the scale
  ind = ~np.isnan(pos);            # index for outliers
  diff_corr = diff*dEref/dE;
  disp= np.poly1d(np.polyfit(pos[ind],diff_corr[ind],order));

  if verbosity > 1:
    plt.gca().plot(pos[~ind],diff[~ind], 'rx',  label="outliers");
    plt.gca().plot(disp(np.arange(4096)), 'k-', label="fit");
    ax.set_xlabel("position on detector [px]");
    if correctE: 
      ax.set_ylabel("corrected ZLP distance  $ \Delta x * (\Delta E_{ref} / \Delta E)$ [px]");
    else: 
      ax.set_ylabel("ZLP distance dx [px]");
    #ax.set_ylim(460,475);
    ax.legend(loc=0);

  # calculate energy dispersion dE/dy
  e2x=trafo.NonlinearDispersion(disp,scale=dEref,xrange=(0,4096));#

  # DEBUG: test energy calibration for positions of ZLP:
  if verbosity>2:
    fig = plt.figure(); ax=fig.add_subplot(111);
    # reference curve for beam energy
    Beam= BeamEnergy();
    ax.plot(Beam.E,Beam.E_corr,'k-',label='reference');

    # beam energy from ZLP position and energy dispersion      
    for i,(Eref,zl) in enumerate(data):

      E,_    = e2x.inverse(zl[:,0],zl[:,0]); 
      dE     =-(E    - E[0]);         # shift of ZLP from calibrated E-axis
      E_corr = dE + Beam.En(Eref[0]); # corrected Beam energy for each ZLP
      ax.plot(Eref,E_corr-Eref,'x',label=spectra[i].split('/')[-1]);
      ax.set_xlabel("nominal beam energy offset $E_0$ [eV]");
      ax.set_ylabel("estimated deviation $E - E_0$ [eV]");
      ax.set_ylim(-0.3,0.3);
      ax.legend(loc=0);

  # output transformation
  if verbosity > 1:
    print "Transformation to linear energy axis e2x: \n", e2x.info(3);
  return e2x if not ret_data else (e2x, np.vstack((pos[ind],diff_corr[ind])));
Example #5
0
# calculate dispersion
spectra = ["../tests/Eseries%i.tif" % i for i in range(1, 4)]
refname = "../tests/Ereference.msa"
order = 2
DE = None
# energy width of shifted scale [eV]
e2x = get_dispersion(spectra, refname, verbosity=0, order=order, ampl_cut=0.5)

# calculate energy of ZLP for each of the series
E_ref = []
E_zl = []
E_pl = []
for i, filename in enumerate(spectra):

    E,zl,pl=get_peak_pos(filename, refname, border=100, \
                ampl_cut=0.1, verbosity=2)
    # positions [px]
    ind = ~np.isnan(pl[:, 0] - zl[:, 0])
    # identify NaN's
    E = E[ind]
    zl = zl[ind]
    pl = pl[ind]
    zl = e2x.inverse(zl[:, 0], zl[:, 0])[0]
    # energies  [eV]
    pl = e2x.inverse(pl[:, 0], pl[:, 0])[0]
    s0 = np.nanargmax(zl)
    # remove offset
    E_zl.append(E[s0] + zl[s0] - zl)
    plOffset = np.mean(pl-zl) \
             if DE is None else DE
    E_pl.append(E[s0] + zl[s0] + plOffset - pl)
Example #6
0
def get_dispersion(spectra, refname, DE=None, order=2, \
                      ret_data=False, verbosity=0, **kwargs):
  """
    calculate the position-dependent energy dispersion from 
    the distance between two peaks (ZLP and plasmon reference)

    spectra  ... file containing the spectrum image (Nspectra, Npx)
    refname  ... filename of reference spectrum for second peak
    DE       ... (opt) use energy width of shifted scale to calibrate 
                 the absolute energy axis, otherwise we use the nominal
                 distance of two ZLPs
    order    ... (opt) order of the fitting polynomial for the distances
    ret_data ... (opt) return (pos, diff)-points used for the fit
    verbosity... (opt) verbosity (0=silent, 2=plotting fit, 3=debug)

    RETURNS function object returning the energy dispersion for any 
            position on the detector
  """

  pos = []; diff = []; data = [];
  for filename in spectra:
   
    Eref,zl,pl  = get_peak_pos( filename, refname, verbosity=verbosity, **kwargs);
    # order zl and pl (zl should be left peak)
    if np.nansum(pl[:,0]-zl[:,0])<0:  zl,pl=pl,zl
   
    # remove NaN's in zl (fit failed) and BeamEnergy (no correction available)
    ind =~np.isnan(zl[:,0] + BeamEnergy().En(Eref));
    Eref=Eref[ind]; zl=zl[ind]; pl=pl[ind];

    # determine position-dependent peak distance
    data.append( [Eref,  zl, pl] );        # zl[:,0]=mean pos
    pos.append( (zl[:,0] + pl[:,0])/2 );   # average position on detector
    diff.append( pl[:,0] - zl[:,0] );      # distance between peaks dy
   
  # plotting
  if verbosity > 1:
    fig=plt.figure(); ax=plt.gca();
    plt.title("Position-dependent peak distance");
    for i in range(len(pos)):
      ax.plot(pos[i],diff[i], 'x', label="%s"%spectra[i].split('/')[-1].split('.dm3')[0]);
 
  # flatten lists (one level)
  pos  = np.asarray(sum(map(list,pos ),[])); 
  diff = np.asarray(sum(map(list,diff),[]));
  
  # fitting polynomial to the appearent size of the scale
  ind = ~np.isnan(pos);            # index for outliers
  disp= np.poly1d(np.polyfit(pos[ind],diff[ind],order));

  if verbosity > 1:
    plt.gca().plot(pos[~ind],diff[~ind], 'rx', label="outliers");
    plt.gca().plot(disp(np.arange(4096)), 'k-', label="fit");
    ax.set_xlabel("position on detector [px]");
    ax.set_ylabel("distance between ZLP and plasmon [px]");
    #ax.set_ylim(460,475);
    ax.legend(loc=0);
 
  # Calculate energy width of scale from the linear shift of the ZLP 
  # with (corrected) beam energy
  if DE is None:

    # for each data series with N shifted spectra, we calculate the
    # missing scaling factor DE from the expected shift of the ZLPs
    DE = [];
    for Eref,zl,pl in data:   #  data[series][quantity][Nspectra]
      
      # expected energy shift for n-th ZLP compared to initial pos.
      Ecorr=-BeamEnergy().En(Eref); # energy scale inversed wrt. E-q map
      Y    =Ecorr[0]-Ecorr[1:];
      # measured energy shift for scale DE=1
      e2x=NonlinearDispersion(disp,scale=1);
      en =e2x.inverse(zl[:,0],zl[:,0])[0]; 
      X  = en[0]-en[1:];
      #print Ecorr, Eref, Y, zl[:,0], en;
      # calculate scaling factor DE which minimizes residuals X*DE-Y
      # min(|X*DE-Y|^2) <=> 2(X*DE-Y)*X = 0 <=> DE=X*Y/X*X
      DE.append( np.dot(X,Y)/np.dot(X,X) );
      
    # ouput
    if verbosity>0:
      print "-- estimating scale DE ---------------------------"
      if verbosity>1:
        for i in range(len(spectra)):
          print "   optimal Eplasmon for '%s':   %8.5f eV " \
                  % (spectra[i].split('/')[-1], DE[i]);
      print "\n Average Eplasmon:  (%5.3f +- %5.3f) eV" \
                  % (np.mean(DE), np.std(DE));

  # calculate energy dispersion dE/dy
  e2x=NonlinearDispersion(disp,scale=np.mean(DE));

  # DEBUG: test energy calibration for positions of ZLP:
  if verbosity>2:
    fig = plt.figure(); ax=fig.add_subplot(111);
    # reference curve for beam energy
    Beam= BeamEnergy();
    ax.plot(Beam.E,Beam.E_corr,'k-',label='reference');

    # beam energy from ZLP position and energy dispersion      
    for i,(Eref,zl,pl) in enumerate(data):

      E,_    = e2x.inverse(zl[:,0],zl[:,0]); 
      dE     =-(E    - E[0]);         # shift of ZLP from calibrated E-axis
      E_corr = dE + Beam.En(Eref[0]); # corrected Beam energy for each ZLP
      ax.plot(Eref,E_corr-Eref,'x',label=spectra[i].split('/')[-1]);
      ax.set_xlabel("nominal beam energy offset $E_0$ [eV]");
      ax.set_ylabel("estimated deviation $E - E_0$ [eV]");
      ax.set_ylim(-0.3,0.3);
      ax.legend(loc=0);

  # output transformation
  if verbosity > 1:
    print "Transformation to linear energy axis e2x: \n", e2x.info(3);
  return e2x if not ret_data else (e2x, np.vstack((pos[ind],diff[ind])));