ARPES: E(deg) to E(K)

I usually use the code that I'll share below to obtain the final ARPES image. 

The problem is when the data contains many points it takes a lot of time to do it.

I'll be grateful if you could help with ideas. 

 

Here is the code: 

#pragma rtGlobals=1        // Use modern global access method.

#include <XYZtoMatrix>

Function/S ARPES(DataWave)
    Wave DataWave;

// Duplicate the wave for backup, and rename it to RealSpace
    Duplicate DataWave RealSpace;


// Determine the size of two dimensional wave
    Variable NumberOfRow;
    Variable NumberOfColumn;
    NumberOfRow = DimSize(RealSpace, 0);
    NumberOfColumn = DimSize(RealSpace, 1);
    
// Rescale the 'Scaled dimension index' of YScale into rad
    Variable YScaleOffset;
    YScaleOffset = DimOffset(RealSpace, 1);
    YScaleOffset = Pi * YScaleOffset / 180;
    
    Variable YScaleDelta;
    YScaleDelta = DimDelta(RealSpace, 1);
    YScaleDelta = Pi * YScaleDelta / 180;
    
    SetScale/P y YScaleOffset, YScaleDelta, "rad", RealSpace;
    
// Extract the rad values to a new wave
    Make/N=(NumberOfColumn)/D RadValue;
    RadValue = YScaleOffset + p*YScaleDelta;
    
// Determine the DimOffset and DimDelta of Column
    Variable XScaleOffset;
    Variable XScaleDelta;
    
    XScaleOffset = DimOffset(RealSpace, 0);
    XScaleDelta = Dimdelta(RealSpace, 0);


// Length of k_space vectors
    Variable LengthOfKSpaceWave;
    LengthOfKSpaceWave = NumberOfRow * NumberOfColumn;
    
// Create three waves of length LengthOfKSpaceWave for E_B, K_parallel, and Counts
    Make/N=(LengthOfKSpaceWave)/D E_B;
    Make/N=(LengthOfKSpaceWave)/D K_parallel;
    Make/N=(LengthOfKSpaceWave)/D Counts;
    
    Variable ii;
    Variable jj;
    Variable a;
    Variable E_F;
    E_F = 0;
    
    Prompt E_F, " Enter Fermi enegy"
    //Prompt Field_Oe, "Applied magnetic field (Oe):"
    DoPrompt "Please enter the Fermi enegy (eV)", E_F;
    
    For(jj=0; jj<(NumberOfRow); jj+=1)
        For(ii=0; ii<(NumberOfColumn); ii+=1)
        a = (jj*NumberOfColumn) + ii;
        K_parallel[a] = 0.512*Sqrt(XScaleOffset + XScaleDelta*jj)*Sin(RadValue[ii]);
        E_B[a] = (XScaleOffset + XScaleDelta*jj) - E_F;
        Counts[a] = RealSpace[jj][ii];
        EndFor
    //String NewCountWaveName = "Counts_"+ num2str(ii);
    //String NewK_parallelWaveName = "K_parallel_" + num2str(ii);
    
        
    //Rename K_parallel, $NewK_parallelWaveName;
    //Rename Counts, $NewCountWaveName;
    EndFor
    //KillWaves NewK_parallelWaveName, NewCountWaveName;
    KillWaves RadValue;
    KillWaves RealSpace;
    
    Display K_parallel vs E_B;
    ModifyGraph mode = 3, Marker = 19;
    ModifyGraph zColor(K_parallel)={Counts,*,*,Grays,0};
    Label left "K_parallel";DelayUpdate
    Label bottom "E_B (eV)"
    
    End

Can you edit this code and put it inside the Code sniped block? It will be much easier to read. 

My best guess is that most time is spent inside the two embedded for loops. If this can be written as Igor wave line, it could be called multithreaded. If it cannot be written as one line, then may be as threadsafe function and called multithreaded. You would get much higher performance in either case. I do not think rest of the code takes much time. 

There are few tricks you can surely make easier - there is no need for K_parallel, E_B, and Counts to be 1D vectors in that calculation, you can have them as 2D matrix, assign values in each [p][q] and convert to vector outside the loop by smart use of redimension. That will make it possible to write each as simple wave assigmnent (single line). 

I think these: (XScaleOffset + XScaleDelta*jj) are simply X value which Igor gives you already? If not, why not make it somehow... 

Use free waves, they seem to be faster to deal with and no need to kill them eventually. 

#pragma rtGlobals=1        // Use modern global access method.

#include <XYZtoMatrix>

Function/S ARPES(DataWave)
    Wave DataWave;

// Duplicate the wave for backup, and rename it to RealSpace
    Duplicate DataWave RealSpace;


// Determine the size of two dimensional wave
    Variable NumberOfRow;
    Variable NumberOfColumn;
    NumberOfRow = DimSize(RealSpace, 0);
    NumberOfColumn = DimSize(RealSpace, 1);
   
// Rescale the 'Scaled dimension index' of YScale into rad
    Variable YScaleOffset;
    YScaleOffset = DimOffset(RealSpace, 1);
    YScaleOffset = Pi * YScaleOffset / 180;
   
    Variable YScaleDelta;
    YScaleDelta = DimDelta(RealSpace, 1);
    YScaleDelta = Pi * YScaleDelta / 180;
   
    SetScale/P y YScaleOffset, YScaleDelta, "rad", RealSpace;
   
// Extract the rad values to a new wave
    Make/N=(NumberOfColumn)/D RadValue;
    RadValue = YScaleOffset + p*YScaleDelta;
   
// Determine the DimOffset and DimDelta of Column
    Variable XScaleOffset;
    Variable XScaleDelta;
   
    XScaleOffset = DimOffset(RealSpace, 0);
    XScaleDelta = Dimdelta(RealSpace, 0);


// Length of k_space vectors
    Variable LengthOfKSpaceWave;
    LengthOfKSpaceWave = NumberOfRow * NumberOfColumn;
   
// Create three waves of length LengthOfKSpaceWave for E_B, K_parallel, and Counts
    Make/N=(LengthOfKSpaceWave)/D E_B;
    Make/N=(LengthOfKSpaceWave)/D K_parallel;
    Make/N=(LengthOfKSpaceWave)/D Counts;
   
    Variable ii;
    Variable jj;
    Variable a;
    Variable E_F;
    E_F = 0;
   
    Prompt E_F, " Enter Fermi enegy"
    //Prompt Field_Oe, "Applied magnetic field (Oe):"
    DoPrompt "Please enter the Fermi enegy (eV)", E_F;
   
    For(jj=0; jj<(NumberOfRow); jj+=1)
        For(ii=0; ii<(NumberOfColumn); ii+=1)
        a = (jj*NumberOfColumn) + ii;
        K_parallel[a] = 0.512*Sqrt(XScaleOffset + XScaleDelta*jj)*Sin(RadValue[ii]);
        E_B[a] = (XScaleOffset + XScaleDelta*jj) - E_F;
        Counts[a] = RealSpace[jj][ii];
        EndFor
    //String NewCountWaveName = "Counts_"+ num2str(ii);
    //String NewK_parallelWaveName = "K_parallel_" + num2str(ii);
   
       
    //Rename K_parallel, $NewK_parallelWaveName;
    //Rename Counts, $NewCountWaveName;
    EndFor
    //KillWaves NewK_parallelWaveName, NewCountWaveName;
    KillWaves RadValue;
    KillWaves RealSpace;
   
    Display K_parallel vs E_B;
    ModifyGraph mode = 3, Marker = 19;
    ModifyGraph zColor(K_parallel)={Counts,*,*,Grays,0};
    Label left "K_parallel";DelayUpdate
    Label bottom "E_B (eV)"
   
    End

The first part was easy.

Andy

In reply to by ilavsky

#pragma rtGlobals=1     // Use modern global access method.

#include <XYZtoMatrix>

Function/S ARPES(DataWave)
    Wave DataWave;



// Duplicate the wave for backup, and rename it to RealSpace
    Duplicate DataWave RealSpace;


// Determine the size of two dimensional wave
    Variable NumberOfRow;
    Variable NumberOfColumn;
    NumberOfRow = DimSize(RealSpace, 0);
    NumberOfColumn = DimSize(RealSpace, 1);
   
// Rescale the 'Scaled dimension index' of YScale into rad
    Variable YScaleOffset;
    YScaleOffset = DimOffset(RealSpace, 1);
    YScaleOffset = Pi * YScaleOffset / 180;
   
    Variable YScaleDelta;
    YScaleDelta = DimDelta(RealSpace, 1);
    YScaleDelta = Pi * YScaleDelta / 180;
   
    SetScale/P y YScaleOffset, YScaleDelta, "rad", RealSpace;
   
// Extract the rad values to a new wave
    Make/N=(NumberOfColumn)/D RadValue;
    RadValue = YScaleOffset + p*YScaleDelta;
   
// Determine the DimOffset and DimDelta of Column
    Variable XScaleOffset;
    Variable XScaleDelta;
   
    XScaleOffset = DimOffset(RealSpace, 0);
    XScaleDelta = Dimdelta(RealSpace, 0);


// Length of k_space vectors
    Variable LengthOfKSpaceWave;
    LengthOfKSpaceWave = NumberOfRow * NumberOfColumn;
   
// Create three waves of length LengthOfKSpaceWave for E_B, K_parallel, and Counts
    Make/N=(LengthOfKSpaceWave)/D E_B;
    Make/N=(LengthOfKSpaceWave)/D K_parallel;
    Make/N=(LengthOfKSpaceWave)/D Counts;
   
    Variable ii;
    Variable jj;
    Variable a;
    Variable E_F;
    E_F = 0;
   
    Prompt E_F, " Enter Fermi enegy"
    //Prompt Field_Oe, "Applied magnetic field (Oe):"
    DoPrompt "Please enter the Fermi enegy (eV)", E_F;
   
    For(jj=0; jj<(NumberOfRow); jj+=1)
        For(ii=0; ii<(NumberOfColumn); ii+=1)
        a = (jj*NumberOfColumn) + ii;
        K_parallel[a] = 0.512*Sqrt(XScaleOffset + XScaleDelta*jj)*Sin(RadValue[ii]);
        E_B[a] = (XScaleOffset + XScaleDelta*jj) - E_F;
        Counts[a] = RealSpace[jj][ii];
        EndFor
    //String NewCountWaveName = "Counts_"+ num2str(ii);
    //String NewK_parallelWaveName = "K_parallel_" + num2str(ii);
   
       
    //Rename K_parallel, $NewK_parallelWaveName;
    //Rename Counts, $NewCountWaveName;
    EndFor
    //KillWaves NewK_parallelWaveName, NewCountWaveName;
    KillWaves RadValue;
    KillWaves RealSpace;
   
    Display K_parallel vs E_B;
    ModifyGraph mode = 3, Marker = 19;
    ModifyGraph zColor(K_parallel)={Counts,*,*,Grays,0};
    Label left "K_parallel";DelayUpdate
    Label bottom "E_B (eV)"
   
    End

 

This is my wild guess without testing... This is for K_parallel only, the E_B is similar. But I think this like this:

Duplicate/O DataWave, K_parallel  - you will have 2D K_parallel with the same x and y scaling as the data.  

Then I think your two nested loops do simply    
K_parallel = 0.512*sqrt(x)*sin(y)    
I may be wrong, but I think this is what you are doing. And if this takes too long, do this in with multithread keyword in front of the equation and it will run on all cores you have: 
multithread K_parallel = 0.512*sqrt(x)*sin(y)    

After that you do
Redimension/N=1 K_parallel
and it will convert 2D K_parallel into 1D K_parallel  (which is anyway how Igor orders the data internally, check the manual for how this is done, it is well explained there in About Waves I think). If the order of Columns/rows in 1D output is wrong, you may need to swap the K_parallel columns/rows first using MatrixOp operation.  

You can use interp2D effectively to convert angles to k values of an ARPES map (2D->2D). Here is the rusty code from my university days which does that. It may not work out of the box (I haven't looked into your code what your data looks like), but should give you an idea. It does not get rid of the nested loop, but I guess you can optimize this code for speed in many places.

Function AngleToK(inwave)
    Wave inwave

    Variable rows,columns,xdelta,xoffset,ydelta,yoffset    
    rows    = DimSize(inwave,0)
    columns = DimSize(inwave,1)
    xdelta  = DimDelta(inwave,0)
    xoffset = DimOffset(inwave,0)
    ydelta  = DimDelta(inwave,1)
    yoffset = DimOffset(inwave,1)
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Variable kmin,kmax,kdelta,Emax,prec = 2
    Emax    = xoffset + xdelta*(rows-1)
    kmin    = 0.512*sqrt(Emax)*sin(pi/180*(yoffset))
    kmax    = 0.512*sqrt(Emax)*sin(pi/180*(yoffset+(columns-1)*ydelta))
   
    String NewName = NameofWave(inwave)+"_k"
    Make/O/N=(rows,prec*columns) $NewName
    Wave outwave = $NewName
   
    SetScale/P  x xoffset,xdelta, WaveUnits(inwave,0), outwave
    SetScale/I  y kmin,kmax, "k [Å-1]", outwave
    kdelta = DimDelta(outwave,1)
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Variable i,j,IPx,IPy, value
    for (i = 0; i < rows; i += 1)
        for (j = 0; j < prec*columns; j += 1)
            IPx = xoffset + i*xdelta
            IPy = 180/pi*asin( (kmin+j*kdelta) / ( 0.512*sqrt(IPx) ) )
            value = interp2D(inwave, IPx, IPy)
            if (value < 0 || value > 0)
                outwave[i][j] = value
            endif
        endfor
    endfor
End