#pragma rtGlobals=1 // Use modern global access method. #pragma ModuleName=EnergyWireScans #pragma version = 0.1 #include "WinView", version>=1.6 #include "ImageDisplayScaling", version>=1.2 #include "microGeometry", version>=1.0 Static Constant hc = 1.239841857 // hc (keV-nm) Static Constant secPerPixelFixed = 18.8e-6 // the fixed time is takes to process one pixel (sec) after any distortion Static Constant secPerPixelDistort = 15.24e-3 // time is takes to process distortion for one pixel (sec) (measured with a 2GHz clock) // ** To get the ful chip unbinned pixel position from ROI data use the following: // ** fullchip x = groupx*x + (groupx-1)/2 + (startx - 1) // ** fullchip y = groupy*y + (groupy-1)/2 + (starty - 1) // py = j*groupy + (groupy-1)/2 + (starty-1) // px = i*groupx + (groupx-1)/2 + (startx-1) // // // at angle=0, F=-Y, H=+Z taken right out of focus5.5.st // F = -Y*cos(angle) + Z*sin(angle) // H = Y*sin(angle) + Z*cos(angle) // Y = H*sin(angle) - F*cos(angle) // Z = H*cos(angle) + F*sin(angle) // // // new for May 3, 2006 // sz = sample z position // s = Æz/Æy of ray from sample to pixel on CCD // // z = ( zs - s*Fo/cos(angle) - R*sqrt(1-s*s) ) / (1-s*tan(angle) ) // H = [z/cos(angle) - Fo*tan(angle)]] = [z - Fo*sin(angle)] / cos(angle) // // for a Gaussian, fwhm = 2*K3*sqrt(ln(2)) // for a Lorentzian, fwhm = 2*sqrt(K3) Menu "micro" Submenu "Energy-Wire Scans" "New Energy-Wire scan...",NewEnergyWireScan("","",NaN) "run Fill E vs Depth",Fill_EvsDepth("imagePath","") "run Fill Q vs Depth",Fill_QvsDepth(NaN,"imagePath","","","") " compute & fit Q at 1 depth",fitQat1depth(NaN) "-" "Graph Intensity on [keV x Depth]",MakeGraph_keV_Depth() "Graph Intensity on [Q x Depth]",MakeGraph_Q_Depth() " Graph 1 Q historam",MakeGraph_Qhist() End Submenu "Set Igor Data Folder" FolderInMenuItem(0) ,/Q, SetDataFolderFromMenu(0) // set to data folder 0 FolderInMenuItem(1) ,/Q, SetDataFolderFromMenu(1) // set to data folder 1 FolderInMenuItem(2) ,/Q, SetDataFolderFromMenu(2) // set to data folder 2 FolderInMenuItem(3) ,/Q, SetDataFolderFromMenu(3) // set to data folder 3 FolderInMenuItem(4) ,/Q, SetDataFolderFromMenu(4) // set to data folder 4 FolderInMenuItem(5) ,/Q, SetDataFolderFromMenu(5) // set to data folder 5 FolderInMenuItem(6) ,/Q, SetDataFolderFromMenu(6) // set to data folder 6 FolderInMenuItem(7) ,/Q, SetDataFolderFromMenu(7) // set to data folder 7 FolderInMenuItem(8) ,/Q, SetDataFolderFromMenu(8) // set to data folder 8 FolderInMenuItem(9) ,/Q, SetDataFolderFromMenu(9) // set to data folder 9 FolderInMenuItem(10) ,/Q, SetDataFolderFromMenu(10) // set to data folder 10 FolderInMenuItem(11) ,/Q, SetDataFolderFromMenu(11) // etc. ... FolderInMenuItem(12) ,/Q, SetDataFolderFromMenu(12) FolderInMenuItem(13) ,/Q, SetDataFolderFromMenu(13) FolderInMenuItem(14) ,/Q, SetDataFolderFromMenu(14) FolderInMenuItem(15) ,/Q, SetDataFolderFromMenu(15) FolderInMenuItem(16) ,/Q, SetDataFolderFromMenu(16) FolderInMenuItem(17) ,/Q, SetDataFolderFromMenu(17) FolderInMenuItem(18) ,/Q, SetDataFolderFromMenu(18) FolderInMenuItem(19) ,/Q, SetDataFolderFromMenu(19) FolderInMenuItem(20) ,/Q, SetDataFolderFromMenu(20) FolderInMenuItem(21) ,/Q, SetDataFolderFromMenu(21) FolderInMenuItem(22) ,/Q, SetDataFolderFromMenu(22) FolderInMenuItem(23) ,/Q, SetDataFolderFromMenu(23) FolderInMenuItem(24) ,/Q, SetDataFolderFromMenu(24) FolderInMenuItem(25) ,/Q, SetDataFolderFromMenu(25) FolderInMenuItem(26) ,/Q, SetDataFolderFromMenu(26) FolderInMenuItem(27) ,/Q, SetDataFolderFromMenu(27) FolderInMenuItem(28) ,/Q, SetDataFolderFromMenu(28) FolderInMenuItem(29) ,/Q, SetDataFolderFromMenu(29) FolderInMenuItem(30) ,/Q, SetDataFolderFromMenu(30) FolderInMenuItem(31) ,/Q, SetDataFolderFromMenu(31) FolderInMenuItem(32) ,/Q, SetDataFolderFromMenu(32) FolderInMenuItem(33) ,/Q, SetDataFolderFromMenu(33) FolderInMenuItem(34) ,/Q, SetDataFolderFromMenu(34) FolderInMenuItem(35) ,/Q, SetDataFolderFromMenu(35) FolderInMenuItem(36) ,/Q, SetDataFolderFromMenu(36) FolderInMenuItem(37) ,/Q, SetDataFolderFromMenu(37) FolderInMenuItem(38) ,/Q, SetDataFolderFromMenu(38) FolderInMenuItem(39) ,/Q, SetDataFolderFromMenu(39) FolderInMenuItem(40) ,/Q, SetDataFolderFromMenu(40) End End // Function/S FolderInMenuItem(m) // note, "!\022(" causes a menu item to be checked and disabled Variable m // index to the folder, starts with 0 if (m<1) Variable i,N=CountObjects("root:", 4 ) String list="", fldr="" for (i=0;i '%s'\r",from,to End Function/T requestFileRoot(pathName,suffixes) String pathName Variable suffixes // number of suffixes to remove, each suffix looks like "_12", leave the underscore Variable refNum PathInfo $pathName pathName = SelectString(V_flag,"",pathName) String message="pick any reconstructed spe file in range, using datafolder = "+GetDataFolder(0) Open/T=".SPE"/D/M=message/P=$pathName/R refNum if (strlen(S_fileName)<1) return "" endif String fullPath = ParseFilePath(1,S_fileName,":",1,0) String name = ParseFilePath(3,S_fileName,":",0,0) String fileRoot=name if (suffixes<1) // nothing to do return fullPath+";"+fileRoot endif for (;suffixes>1;suffixes-=1) // strip off all but last suffix fileRoot = fileRoot[0,strsearch(fileRoot,"_",Inf,1)-1] endfor // now we have fileRoot is the desired fileRoot with some digits following it, so remove all trailing digits Variable i for (i=strlen(fileRoot)-1;isdigit(fileRoot[i])&&i>=0;i-=1) endfor return fullPath+";"+fileRoot[0,i] End // Static Function isdigit(c) String c Variable i=char2num(c) return (48<=i && i<=57) End Function fitQat1depth(row) Variable row // row index in Q_Depth[row][], the first index is depth if (!(row>=0)) row = 8 Prompt row, "row index for constant depth cut" DoPrompt "row index",row if (V_flag) return 1 endif endif Wave Q_Depth=Q_Depth, Qhist=Qhist Qhist = Q_Depth[row][p] // select the row CurveFit/Q lor Qhist/D Variable fwhm = 2*sqrt(K3) // for Lorentzian printf "at peak, Q = %.5g(1/nm), fwhm = %.2g(1/nm), fwhm/Q = %.1e, chisq=%.3g\r",K2,fwhm, fwhm/K2,V_chisq End // fileRoot="Macintosh HD:Users:tischler:data:Sector 34:July 4, 2006:EW5:recon:EW5_1_" // FillQhist("Macintosh HD:Users:tischler:data:Sector 34:July 4, 2006:EW5:recon:EW5_1_","0-2") // Fill_QvsDepth("Macintosh HD:Users:tischler:data:Sector 34:July 4, 2006:EW5:recon:EW5_","1,22,43,64,85,106,127,148,169,190,211,232,253,274,295,316,337,358,379","0-60") // Fill_QvsDepth("Macintosh HD:Users:tischler:data:Sector 34:July 4, 2006:EW5:recon:EW5_","211,232","15-22") // process an E-W scan that has been already depth sorted Function Fill_QvsDepth(d0,pathName,namePart,range1,range2) Variable d0 // d-spacing of the strain=0 material (nm) String pathName // either name of path to images, or the full expliiciit path, i.e. "Macintosh HD:Users:tischler:data:cal:recon:" String namePart // the first part of file name, something like "EW5_" String range1 // range of first number after file root (designates energy) String range2 // range of second numbers after range1 (designates depth) String str PathInfo $pathName if (!V_flag || strlen(namePart)<1) // path does not exist or no namePart, ask user String pathPart str = requestFileRoot(pathName,2) pathPart = StringFromList(0,str) namePart = StringFromList(1,str) if (strlen(S_path)<1 || strlen(namePart)<1) return 1 // invalid inputs endif if (!stringmatch(pathPart,S_path)) // path was changed if (stringmatch(pathName,"imagePath")) NewPath/O/M="path to reconstructed spe files" imagePath pathPart // for iamgePath, automatically reassign else NewPath/M="path to reconstructed spe files" $pathName pathPart // for other names, ask endif endif endif PathInfo $pathName if (strlen(S_path)<1 || strlen(namePart)<1) return 1 // invalid inputs endif if (ItemsInList(GetRTStackInfo(0))<2) printf "using data from files starting with '%s'\r",S_path+namePart endif if (strlen(range1)<=0 || strlen(range2)<=0) // if either range is empty, get the full range str = get_ranges(pathName,namePart) if (strlen(range1)<=0) range1=StringFromList(0,str) if (ItemsInList(GetRTStackInfo(0))<2) printf "setting energy range to '%s'\r",range1 endif endif if (strlen(range2)<=0) range2=StringFromList(1,str) if (ItemsInList(GetRTStackInfo(0))<2) printf "setting depth range to '%s'\r",range2 endif endif endif String fileRoot // complete path up to and including the unerscore PathInfo $pathName if (V_flag) // if path exists, reset pathName to the explicit path, otherwise assume it is the explicit path fileRoot = S_path elseif (strsearch(pathName,":",Inf,1) == strlen(pathName)-1) fileRoot = pathName+":" // ensure terminating colon endif fileRoot += namePart if (!((d0>0))) // invalid d0, check for a local value d0 = NumVarOrDefault("d0",NaN) endif Variable timer = startMSTimer // start timer for initial processing STRUCT microGeometry geo FillGeometryStructDefault(geo) Variable useDistortion = NumVarOrDefault("root:Packages:geometry:useDistortion",USE_DISTORTION_DEFAULT) print "processing with distotion "+SelectString(useDistortion,"OFF","on") KIllWaves/Z root:Packages:geometry:tempCachedDistortionMap // ensure that this is gone! String name sprintf name, "%s%d_%d.SPE",fileRoot,str2num(range1),str2num(range2) Wave image = $(LoadWinViewFile(name)) // load first image in both ranges if (!WaveExists(image)) printf "could not load very first image named '%s'\r",name DoAlert 0,"could not load very first image" return 1 endif Variable Npixels = numpnts(image) // number of pixels in one image String wnote = note(image) KillWaves/Z image Variable keV, yc, startx, endx, starty, endy, groupx, groupy keV = NumberByKey("keV", wnote,"=") // energy for this image yc = NumberByKey("yc", wnote,"=") // yc for this image startx = NumberByKey("startx", wnote,"=") endx = NumberByKey("endx", wnote,"=") groupx = NumberByKey("groupx", wnote,"=") starty = NumberByKey("starty", wnote,"=") endy = NumberByKey("endy", wnote,"=") groupy = NumberByKey("groupy", wnote,"=") if (numtype(startx+endx+starty+endy+groupx+groupy)) DoAlert 0,"could not get ROI from wave note of image '"+name+"'" return 1 elseif (numtype(yc+keV)) DoAlert 0,"invalid yc or keV in image '"+name+"'" return 1 endif String strStruct // string to hold geo for an easy copying StructPut/S/B=0 geo, strStruct // this is done to copy geo into geoLocal STRUCT microGeometry geoLocal // the local geo with yc of this image StructGet/S/B=0 geoLocal, strStruct // now we can change yc in geoLocal without making trouble above Variable NkeV=ItemsInRange(range1), Ndepth=ItemsInRange(range2) Variable N = Ndepth*NkeV // number of images to be processed Variable Q0 = 2*PI/d0 // Q of unstrained material (1/nm) Make/N=(N)/O/D depth_FillQvsDepth, keV_FillQvsDepth depth_FillQvsDepth = NaN keV_FillQvsDepth = NaN Make/N=(N)/O/U/I m1_FillQvsDepth,m2_FillQvsDepth m1_FillQvsDepth = 0 m2_FillQvsDepth = 0 Variable seconds if (ItemsInList(GetRTStackInfo(0))<2) seconds = Npixels*N*secPerPixelFixed if (useDistortion) seconds += Npixels*secPerPixelDistort endif printf "about to examine %d images, which should take %s, and end at %s\r",N,Secs2Time(seconds,5,1),Secs2Time(DateTime+seconds,1) endif // for all the N files (go over both range1 & range2), store the energies, depths, and indicies Variable m1,m2, i for (m2=str2num(range2), i=0; numtype(m2)==0; m2=NextInRange(range2,m2)) // loop over range2 for (m1=str2num(range1); numtype(m1)==0; m1=NextInRange(range1,m1), i+=1) // loop over range1 sprintf name, "%s%d_%d.SPE",fileRoot,m1,m2 wnote=WinViewReadHeader(name) // wave note to add to file read in if (!sameROI(wnote,startx, endx, starty, endy, groupx, groupy)) // skip bad ROI's continue endif depth_FillQvsDepth[i] = NumberByKey("depthSi", wnote,"=") keV_FillQvsDepth[i] = NumberByKey("keV", wnote,"=") m1_FillQvsDepth[i] = m1 m2_FillQvsDepth[i] = m2 endfor endfor seconds = stopMSTimer(timer)/1e6 if (ItemsInList(GetRTStackInfo(0))<2 && seconds>0.2) printf "first pass to examine headers took %s\r",Secs2Time(seconds,5,3) endif timer = startMSTimer // start a timer on bulk of processing WaveStats/M=1/Q keV_FillQvsDepth Variable ikeVlo=V_minloc, ikeVhi=V_maxloc // save location of min and max energies if (V_numNans) DoAlert 0, "There were "+num2istr(V_numNans)+" bad images found" endif WaveStats/M=1/Q depth_FillQvsDepth Variable depthMin=V_min, depthMax=V_max // depth range Variable dDepth = (depthMax-depthMin)/(Ndepth-1) Variable theta, Qmin, Qmax, dQ, NQ // get range of Q sprintf name, "%s%d_%d.SPE",fileRoot,m1_FillQvsDepth[ikeVlo],m2_FillQvsDepth[ikeVlo] Wave image = $(LoadWinViewFile(name)) // load image geoLocal.ycent = NumberByKey("yc", note(image),"=") // change yc in local copy of geo theta = real(thetaRange(geoLocal,image)) // min theta on this image KillWaves/Z image keV = keV_FillQvsDepth[ikeVlo] Qmin = 4*PI*sin(theta)*keV/hc // min Q (1/nm) sprintf name, "%s%d_%d.SPE",fileRoot,m1_FillQvsDepth[ikeVhi],m2_FillQvsDepth[ikeVhi] Wave image = $(LoadWinViewFile(name)) // load image geoLocal.ycent = NumberByKey("yc", note(image),"=") // change yc in local copy of geo theta = imag(thetaRange(geoLocal,image)) // max theta on this image KillWaves/Z image keV = keV_FillQvsDepth[ikeVhi] Qmax = 4*PI*sin(theta)*keV/hc // max Q (1/nm) // determine dQ (1/nm), the Q resolution to use. Base it on the distance between two adjacent pixels Make/N=3/O/D qhat Variable px,py // the current pixel to analyze (unbinned full chip pixels) py = round((starty+endx)/2) // approximate full chip pixel position in center or the image px = round((starty+endy)/2) dQ = 4*PI*abs(sin(pixel2q(geo,px,py,qhat))-sin(pixel2q(geo,px,py+groupy,qhat)))*keV/hc // dQ /= 1.5 NQ = round((Qmax-Qmin)/dQ) + 1 // number of Qs if (ItemsInList(GetRTStackInfo(0))<2) Variable deV = 1000*(keV_FillQvsDepth[ikeVhi]-keV_FillQvsDepth[ikeVlo])/(NkeV-1) printf "depth range = [%g, %g] (µm), Æz=%.2g µm\r",depthMin,depthMax,dDepth printf "E range = [%g, %g] (keV), ÆE=%.2g eV\r", keV_FillQvsDepth[ikeVlo], keV_FillQvsDepth[ikeVhi],deV printf "Q range = [%g, %g] (1/nm), ÆQ=%.2g(1/nm)\r",Qmin, Qmax,dQ if (d0>0) printf " d0 = %g (nm), Q0 = %g (1/nm)\r",d0,Q0 endif endif Make/N=(Ndepth,NQ)/O/D Q_Depth // make the array to save the Q vs Depth array Q_Depth = 0 Make/N=(NQ)/O/D Qhist // array to hold Q's from one image SetScale/I x depthMin,depthMax,"µm", Q_Depth SetScale/I y Qmin,Qmax,"1/nm", Q_Depth SetScale/I x Qmin,Qmax,"1/nm", Qhist seconds = stopMSTimer(timer)/1e6 if (ItemsInList(GetRTStackInfo(0))<2 && seconds>0.2) printf "setting up arrays took %s\r",Secs2Time(seconds,5,3) endif // done with the setup part, now actually compute something if (useDistortion) // if using the distortion, precompute for all images here Variable j, Ni=(endx-startx+1)/groupx, Nj=(endy-starty+1)/groupy Make/N=(Ni,Nj,2)/O/R root:Packages:geometry:tempCachedDistortionMapTemp Wave distortionMap = root:Packages:geometry:tempCachedDistortionMapTemp SetScale/P x startx,groupx,"", distortionMap // scale distortionMap[][] to full chip pixels SetScale/P y starty,groupy,"", distortionMap Variable/C dxy Wave xymap = root:Packages:geometry:xymap for (j=0;j0) wnote = ReplaceNumberByKey("Q0",wnote,Q0,"=") endif String title = StrVarOrDefault("title","") if (strlen(title)) title = ReplaceString("=",title,"_") // in the wave note the string cannot have "=" or ";" title = ReplaceString(";",title,"_") wnote = ReplaceStringByKey("title",wnote,title,"=") endif Note/K Q_depth, wnote ImageStats/M=1/Q Q_Depth Qhist = Q_Depth[V_maxRowLoc][p] // add Intensity from this image Note/K Qhist, wnote if (ItemsInList(GetRTStackInfo(0))<2) Make/T/O T_Constraints={"K0 > 0","K3 > 0"} // ensure width>0, and base line positive WaveStats/Q Qhist T_Constraints[0] = "K0 > "+num2str(min(0,V_min)) CurveFit/Q lor Qhist/D /C=T_Constraints KillWaves/Z T_Constraints Variable fwhm = 2*sqrt(K3) // for Lorentzian printf "at peak, Q = %.3f(1/nm), fwhm = %.2g(1/nm)",K2,fwhm wnote = ReplaceNumberByKey("Qcenter",wnote,K2,"=") wnote = ReplaceNumberByKey("Qfwhm",wnote,fwhm,"=") if (Q0>0) Variable strain = (K2-Q0)/Q0 printf ", ÆQ = %.3f, strain = %.1e",K2-Q0,strain wnote = ReplaceNumberByKey("ÆQ",wnote,K2-Q0,"=") wnote = ReplaceNumberByKey("strain",wnote,strain,"=") endif Note/K Qhist, wnote printf "\r" MakeGraph_Q_Depth() MakeGraph_Qhist() endif KillWaves/Z depth_FillQvsDepth, keV_FillQvsDepth, m1_FillQvsDepth, m2_FillQvsDepth, qhat, sinTheta return 0 End // Function MakeGraph_Q_Depth() // display a Q_Depth[][] array Wave Q_Depth=Q_Depth String graphName=CleanupName("Graph_"+GetDataFolder(0)+"_Q_Depth",0) String wnote = note(Q_Depth) if (strlen(WinList(graphName,"","WIN:1"))) DoWindow/F $graphName // graph is already up, so just bring it to front elseif (exists(graphName)==5) Execute graphName+"()" // a recreation macro exists, run it else Display /W=(357,62,744,474) // nothing exists, create the graph DoWindow/C $graphName AppendImage Q_Depth ModifyImage Q_Depth ctab= {*,*,Terrain,1} ModifyGraph gfMult=130, tick=2, mirror=1, minor=1, lowTrip=0.001, standoff=0 ModifyGraph axOffset(left)=-1.5 String units = WaveUnits(Q_Depth,1) if (strsearch(units,"1/",0)==0) Label left "Q (\\E"+units[2,Inf]+"\\S-1\\M)" else Label left "Q (\\U)" endif Label bottom "Depth (\\U)" String str = StringByKey("title",wnote,"=")// first try to get title from wave note if (strlen(str)<1) str = StrVarOrDefault("title","") // nothing in wavenote, try the title global string endif str = SelectString(strlen(str),"",str+"\r\\Zr067") str += GetWavesDataFolder(Q_Depth,1)+"\r"+StringByKey("dateExposed",wnote,"=") TextBox/C/N=text0/F=0 str endif Variable iTag = NumberByKey("ImaxPnt",wnote,"=") // point in Q_Depth[][] of the max Variable Q0 = NumberByKey("Q0",wnote,"=") // un-strained Q (1/nm) if (numtype(Q0)==0 && Q0>0) // always redo the line showing Q0 if valid value is passed SetDrawLayer/W=$graphName/K UserFront SetDrawLayer/W=$graphName UserFront SetDrawEnv/W=$graphName xcoord= prel,ycoord= left,dash= 1 DrawLine/W=$graphName 0,Q0,1,Q0 endif if (numtype(iTag)==0 && iTag>0) // for a valid number, redo the peak marker if (WhichListItem("peak0",AnnotationList(graphName))>=0 && stringmatch(StringByKey("TYPE",AnnotationInfo(graphName,"peak0")),"Tag")) Tag/C/N=peak0 Q_Depth, iTag // simply move existing tag to peak else Tag/N=peak0/F=0/A=MB/X=-10/Y=20 Q_Depth, iTag, "\\OZ" endif endif End // Function MakeGraph_Qhist() // display a Qhist[] wave String graphName=CleanupName("Graph_"+GetDataFolder(0)+"_Qhist",0) if (strlen(WinList(graphName,"","WIN:1"))) DoWindow/F $graphName // graph is already up, so just bring it to front return 0 elseif (exists(graphName)==5) Execute graphName+"()" // a recreation macro exists, run it return 0 endif Variable fitExists = exists("fit_Qhist")==1 Display /W=(732,69,1262,429) Qhist DoWindow/C $graphName if (fitExists) AppendToGraph fit_Qhist ModifyGraph lSize(fit_Qhist)=2 endif ModifyGraph gfMult=130, tick=2, mirror=1, minor=1, lowTrip=0.001, standoff=0 ModifyGraph mode(Qhist)=4, marker(Qhist)=19, lStyle(Qhist)=1, rgb(Qhist)=(16385,16388,65535), msize(Qhist)=4, hbFill(Qhist)=4 ModifyGraph axOffset(left)=-3.85714,axOffset(bottom)=-0.461538 Label left "Intensity" String units = WaveUnits(Qhist,0) if (strsearch(units,"1/",0)==0) Label bottom "Q (\\E"+units[2,Inf]+"\\S-1\\M)" else Label bottom "Q (\\U)" endif SetAxis/A/E=1 left ShowInfo String wnote = note(Qhist) String str = StringByKey("title",wnote,"=")// first try to get title from wave note if (strlen(str)<1) str = StrVarOrDefault("title","") // nothing in wavenote, try the title global string endif str = SelectString(strlen(str),"",str+"\r\\Zr075") str += GetWavesDataFolder(Qhist,1)+"\r"+StringByKey("dateExposed",note(Qhist),"=") TextBox/A=RT/C/N=text0/F=0 str Variable Qc = NumberByKey("Qcenter",wnote,"="), fwhm = NumberByKey("Qfwhm",wnote,"=") Variable dQ = NumberByKey("ÆQ",wnote,"="), strain = NumberByKey("strain",wnote,"=") if (numtype(Qc+fwhm)==0) // found valid Q values sprintf str, "Q\\Bc\\M = %g (nm\\S-1\\M)\rFWHM = %.2g",Qc,fwhm TextBox/C/N=textQ/F=0/A=LT str if (numtype(dQ+strain)==0) sprintf str,"strain = %.1e",strain AppendText/N=textQ str endif endif return 0 End // Static Function/S MakeSinThetaArray(fullName,geo) // make an array the same size as an image, but filled with sin(theta)'s String fullName // complete path name to image STRUCT microGeometry &geo // the input geo with the yc of Si calibration standard Variable timer = startMSTimer // start timer for initial processing Wave image = $(LoadWinViewFile(fullName)) // load image if (!WaveExists(image)) printf "could not load image named '%s'\r",fullName DoAlert 0,"could not load image" return "" endif String wnote = note(image) Variable startx, starty, groupx, groupy, yc startx = NumberByKey("startx", wnote,"=") groupx = NumberByKey("groupx", wnote,"=") starty = NumberByKey("starty", wnote,"=") groupy = NumberByKey("groupy", wnote,"=") yc = NumberByKey("yc", wnote,"=") // yc for this image if (numtype(startx+starty+groupx+groupy)) DoAlert 0,"could not get ROI from wave note of image '"+fullName+"'" return "" elseif (numtype(yc)) DoAlert 0,"invalid yc in image '"+fullName+"'" return "" endif String strStruct // string to hold geo for an easy copying StructPut/S/B=0 geo, strStruct // this is done to copy geo into geoLocal STRUCT microGeometry geoLocal // the local geo with yc of this image StructGet/S/B=0 geoLocal, strStruct geoLocal.ycent = yc // change yc in local copy of geo Make/N=(DimSize(image,0),DimSize(image,1))/O/D sinThetaCached Note/K sinThetaCached, wnote CopyScales/I image, sinThetaCached sinThetaCached = NaN // for each pixel in image, compute sin(theta) and save it in sinThetaCached[][] Variable i, j, Ni=DimSize(image,0), Nj=DimSize(image,1) Variable px,py // the current pixel to analyze (unbinned full chip pixels) Make/N=3/O/D qhat for (j=0;j5) Variable depthSi = NumberByKey("depthSi", wnote,"=") printf "filling array of sin(theta) from '%s' at depth = %g, took %s\r",NameOfWave(image),depthSi,Secs2Time(seconds,5,1) endif KIllWaves/Z image, qhat return GetWavesDataFolder(sinThetaCached,2) End //======================================================================================= //======================================================================================= //======================================================================================= // returns true if ROI in list is the same as the other ROI Static Function sameROI(list,startx, endx, starty, endy, groupx, groupy) String list // list with current numbers Variable startx, endx, starty, endy, groupx, groupy // desired numbers Variable ierr = abs(startx-NumberByKey("startx", list,"=")) ierr += abs(endx-NumberByKey("endx", list,"=")) ierr += abs(groupx-NumberByKey("groupx", list,"=")) ierr += abs(starty-NumberByKey("starty", list,"=")) ierr += abs(endy-NumberByKey("endy", list,"=")) ierr += abs(groupy-NumberByKey("groupy", list,"=")) return (ierr < 1e-9) // true if the ROI's are equal End // Static Function/C thetaRange(geo,image) // returns the range of theta spanned by image STRUCT microGeometry &geo Wave image String wnote = note(image) Variable startx, endx, starty, endy startx = NumberByKey("startx", wnote,"=") endx = NumberByKey("endx", wnote,"=") starty = NumberByKey("starty", wnote,"=") endy = NumberByKey("endy", wnote,"=") if (numtype(startx+endx+starty+endy)) DoAlert 0, "could not get ROI from wave note of image '"+NameOfWave(image)+"'" return cmplx(NaN,NaN) endif Make/N=3/O/D qhat Variable theta // Bragg angle (radian) Variable thetaMin, thetaMax // range of theta, test all 4 corners of the image theta = pixel2q(geo,startx-1,starty-1,qhat) thetaMin = theta thetaMax = theta theta = pixel2q(geo,endx-1,starty-1,qhat) thetaMin = min(theta,thetaMin) thetaMax = max(theta,thetaMax) theta = pixel2q(geo,startx-1,endy-1,qhat) thetaMin = min(theta,thetaMin) thetaMax = max(theta,thetaMax) theta = pixel2q(geo,endx-1,endy-1,qhat) thetaMin = min(theta,thetaMin) thetaMax = max(theta,thetaMax) // print thetaMin*180/PI,thetaMax*180/PI KillWaves/Z qhat return cmplx(thetaMin,thetaMax) End Static Function/S FillQhist1image(fullName,geo,sinTheta,Qhist)// fill Qhist from one file, F is for fast, sin(theta) is precomputed String fullName STRUCT microGeometry &geo // the input geo with the yc of Si calibration standard Wave sinTheta // array of sin(theta)'s that is same size as the image to be loaded Wave Qhist Variable timer = startMSTimer // start timer for initial processing Variable sinThetaExists = WaveExists(sinTheta) // use sinTheta[][] if it exists Wave image = $(LoadWinViewFile(fullName)) // load image if (!WaveExists(image)) printf "could not load image named '%s'\r",fullName DoAlert 0,"could not load image" timer = stopMSTimer(timer) // stop timer return "" endif String wnote = note(image) Variable keV = NumberByKey("keV", wnote,"=") Variable startx, starty, groupx, groupy, yc startx = NumberByKey("startx", wnote,"=") groupx = NumberByKey("groupx", wnote,"=") starty = NumberByKey("starty", wnote,"=") groupy = NumberByKey("groupy", wnote,"=") yc = NumberByKey("yc", wnote,"=") // yc for this image keV = NumberByKey("keV", wnote,"=") if (numtype(startx+starty+groupx+groupy)) DoAlert 0,"could not get ROI from wave note of image '"+fullName+"'" timer = stopMSTimer(timer) // stop timer return "" elseif (numtype(yc+keV)) DoAlert 0,"invalid yc or keV in image '"+fullName+"'" timer = stopMSTimer(timer) // stop timer return "" endif ImageStats/M=1/Q image // check for empty images, skip them if (V_min==0 && V_max==0) timer = stopMSTimer(timer) // stop timer wnote = ReplaceNumberByKey("V_min",wnote,V_min,"=") // flag that this is empty wnote = ReplaceNumberByKey("V_max",wnote,V_max,"=") KillWaves/Z image return wnote // image is empty, do not waste time adding zeros endif Variable NQ=numpnts(Qhist) Variable Qmin = DimOffset(Qhist,0) Variable dQ = DimDelta(Qhist,0) String strStruct // string to hold geo for an easy copying StructPut/S/B=0 geo, strStruct // this is done to copy geo into geoLocal STRUCT microGeometry geoLocal // the local geo with yc of this image StructGet/S/B=0 geoLocal, strStruct geoLocal.ycent = yc // change yc in local copy of geo // for each pixel in image, accumulate intensity into Qhist Variable i, j, Ni=DimSize(image,0), Nj=DimSize(image,1) Variable Qval // Q value for one pixel Variable k // index into Qhist for each pixel Variable px,py // the current pixel to analyze (unbinned full chip pixels) Make/N=3/O/D qhat for (j=0;j0) // do not spend time accumulating zeros px = i*groupx + (groupx-1)/2 + (startx-1) if (sinThetaExists) Qval = 4*PI*sinTheta[i][j]*keV/hc // sinTheta[][] exists, use it else Qval = 4*PI*sin(pixel2q(geoLocal,px,py,qhat))*keV/hc // does not exist, so compute theta for each point endif k = limit(round((Qval-Qmin)/dQ),0,NQ-1) Qhist[k] += image[i][j] endif endfor endfor Variable seconds = stopMSTimer(timer)/1e6 // stop timer here if (ItemsInList(GetRTStackInfo(0))<3 && seconds>0.5) printf " processing image '%s' took %s\r",NameOfWave(image),Secs2Time(seconds,5,2) endif KIllWaves/Z image, qhat return wnote End //======================================================================================= //======================================================================================= //======================================================================================= // make the wave keV_Depth[][] which contains the average intensity/pixel for each image as a fn of energy and depth. // it also returns the depth range (as two indices) that contain some intensity (returns NaN for no intensity at all) Function/C Fill_EvsDepth(pathName,namePart) String pathName // probably 'imagePath' String namePart // = "EW5_" String str PathInfo $pathName if (!V_flag || strlen(namePart)<1) // path does not exist or no namePart, ask user String pathPart str = requestFileRoot(pathName,2) pathPart = StringFromList(0,str) namePart = StringFromList(1,str) if (strlen(S_path)<1 || strlen(namePart)<1) return cmplx(NaN,NaN) // invalid inputs endif if (!stringmatch(pathPart,S_path)) // path was changed if (stringmatch(pathName,"imagePath")) NewPath/O/M="path to reconstructed spe files" imagePath pathPart // for iamgePath, automatically reassign else NewPath/M="path to reconstructed spe files" $pathName pathPart // for other names, ask endif endif endif PathInfo $pathName if (strlen(S_path)<1 || strlen(namePart)<1) return cmplx(NaN,NaN) // invalid inputs endif str = get_ranges(pathName,namePart) String range1=StringFromList(0,str), range2=StringFromList(1,str) if (ItemsInList(GetRTStackInfo(0))<2) printf "using data from files starting with '%s'\r",namePart printf "setting energy range to '%s'\r",range1 printf "setting depth range to '%s'\r",range2 endif Variable NkeV = ItemsInRange(range1) Variable Ndepth = ItemsInRange(range2) Make/N=(Ndepth,NkeV)/O keV_Depth // array to recieve the energy vs depth intensity keV_Depth = 0 String fileRoot Variable i,m for (m=str2num(range1), i=0; numtype(m)==0; m=NextInRange(range1,m), i+=1) // loop over all energies which is range1 fileRoot = S_path+namePart+num2istr(m)+"_" ImageIntensityVsDepth(fileRoot,range2) DoUpdate Wave avgIntensity=avgIntensity keV_Depth[][i] += avgIntensity[p] endfor String wnote, name i = str2num(range2) sprintf name, "%s%s%d_%d.SPE",S_path,namePart,str2num(range1),i wnote=WinViewReadHeader(name) // wave note to add to file read in Variable Elo = NumberByKey("keV", wnote,"=") // energy for fiirst image sprintf name, "%s%s%d_%d.SPE",S_path,namePart,lastInRange(range1),i wnote=WinViewReadHeader(name) // wave note to add to file read in Variable Ehi = NumberByKey("keV", wnote,"=")// energy for last image Wave depthIntensity=depthIntensity SetScale/I x depthIntensity[0],depthIntensity[Inf],"µm", keV_Depth SetScale/I y Elo,Ehi,"keV", keV_Depth // find range of depth in keV_Depth[][] that contains all the intensity ImageTransform sumAllRows keV_Depth // this produces the wave W_sumRows[] Wave W_sumRows=W_sumRows // find the range in W_sumRows where there is intensity, outside the range [i1,i2], W_sumRows[] is zero Variable i1,i2 for (i1=0;i1=i1 && W_sumRows[i2]==0;i2-=1) // find the last non-zero values at the end of W_sumRows[] endfor Variable x1=i1*DimDelta(keV_Depth,0)+DimOffset(keV_Depth,0), x2=i2*DimDelta(keV_Depth,0)+DimOffset(keV_Depth,0) if (i1>i2) // special for all zeros i1 = -1 ; i2 = -1 x1 = NaN ; x2 = NaN endif wnote = ReplaceNumberByKey("iDepthRangeLo",wnote,x1,"=") wnote = ReplaceNumberByKey("iDepthRangeHi",wnote,x2,"=") String title = StrVarOrDefault("title","") if (strlen(title)) title = ReplaceString("=",title,"_") // in the wave note the string cannot have "=" or ";" title = ReplaceString(";",title,"_") wnote = ReplaceStringByKey("title",wnote,title,"=") endif Note/K keV_Depth, wnote if (ItemsInList(GetRTStackInfo(0))<2) printf "'%s', contains non-zero intensity in the depth range of [%d, %d] == (%g, %g)(µm)\r",GetWavesDataFolder(keV_Depth,2),i1,i2,x1,x2 MakeGraph_keV_Depth() endif KillWaves/Z W_sumRows return cmplx(i1,i2) End // Function MakeGraph_keV_Depth() // display a keV_Depth[][] array Wave keV_Depth=keV_Depth String graphName=CleanupName("Graph_"+GetDataFolder(0)+"_keV_Depth",0) String wnote = note(keV_Depth) if (strlen(WinList(graphName,"","WIN:1"))) DoWindow/F $graphName // graph is already up, so just bring it to front elseif (exists(graphName)==5) Execute graphName+"()" // a recreation macro exists, run it else Display /W=(8,55,461,422) // nothing exists, create the graph DoWindow/C $graphName AppendImage keV_Depth ModifyImage keV_Depth ctab= {*,*,Terrain,1} ModifyGraph gfMult=130, tick=2, mirror=1, minor=1, lowTrip=0.001, standoff=0 ModifyGraph axOffset(left)=-1.71429,axOffset(bottom)=-0.384615 Label left "Energy (\\U)" Label bottom "Depth (\\U)" String str = StringByKey("title",wnote,"=")// first try to get title from wave note if (strlen(str)<1) str = StrVarOrDefault("title","") // nothing in wavenote, try the title global string endif str = SelectString(strlen(str),"",str+"\r\\Zr067") str += GetWavesDataFolder(keV_Depth,1)+"\r"+StringByKey("dateExposed",wnote,"=") TextBox/C/N=text0/F=0 str endif Variable x1 = NumberByKey("iDepthRangeLo",wnote,"=") // range in depth that contains non-zero values Variable x2 = NumberByKey("iDepthRangeHi",wnote,"=") if (numtype(x1+x2)==0) // always redo the lines showing the range, if x1 & x2 are valid SetDrawLayer/W=$graphName/K UserFront SetDrawLayer/W=$graphName UserFront SetDrawEnv/W=$graphName xcoord= bottom,ycoord= prel,dash= 1 DrawLine/W=$graphName x1,0,x1,1 SetDrawEnv/W=$graphName xcoord= bottom,ycoord= prel,dash= 1 DrawLine/W=$graphName x2,0,x2,1 endif End // for a set of images over a range of depth indicies, make waves showing actual depth, peak intensity, and average intensity Function ImageIntensityVsDepth(fileRoot,range) String fileRoot String range Variable N=ItemsInRange(range) // number of points in range if (N<1) DoAlert 0, "There is nothing in this range, range = '"+range+"'" return 0 endif Variable depth1=NaN,depth2=NaN String name sprintf name, "%s%d.SPE",fileRoot,str2num(range) // open first file in range Wave image = $(LoadWinViewFile(name)) if (!WaveExists(image)) printf "could not load first image named '%s'\r",name Abort "could not load first image" endif depth1 = NumberByKey("depthSi", note(image),"=") KillWaves/Z image sprintf name, "%s%d.SPE",fileRoot,lastInRange(range) // open first file in range Wave image = $(LoadWinViewFile(name)) if (!WaveExists(image)) printf "could not load last image named '%s'\r",name Abort "could not load first image" endif depth2 = NumberByKey("depthSi", note(image),"=") KillWaves/Z image if (numtype(depth1+depth2)) DoAlert 1, "Could not load depth information, this is probably not depth sorted images, contiuing?" if (V_flag!=1) return 0 endif endif Make/N=(N)/O/D maxIntensity,avgIntensity,depthIntensity // wave to hold the result maxIntensity = NaN avgIntensity = NaN depthIntensity = NaN Variable m, i for (m=str2num(range), i=0; numtype(m)==0; m=NextInRange(range,m), i+=1) // loop over all values in range sprintf name, "%s%d.SPE",fileRoot,m Wave image = $(LoadWinViewFile(name)) if (!WaveExists(image)) continue // just skip over missing images printf "could not load image named '%s', continuing ...\r",name endif ImageStats/M=1/Q image maxIntensity[i] = V_max avgIntensity[i] = V_avg depthIntensity[i] = NumberByKey("depthSi", note(image),"=") KIllWaves/Z image endfor return N End Function NewEnergyWireScan(fldr,title,d0) // set up a folder for a new Energy-Wire Scan String fldr // new folder name, it will be at the top level String title // a title to be used on plots Variable d0 // d-spacing of unstrained material (nm) fldr = StringFromList(ItemsInList(fldr,":")-1, fldr,":") // remove leading part of path fldr = CleanupName(fldr,0) // igorize the name String fldrSav= GetDataFolder(1) SetDataFolder root: fldr = SelectString(CheckName(fldr,11),fldr,"") // avoid existing folders SetDataFolder $fldrSav d0 = (d0>0) ? d0 : NaN if (strlen(fldr)<1 || numtype(d0) || strlen(title)<1) // need user input Prompt fldr, "new folder name (no colons)" Prompt d0,"reference d-spacing (nm)" Prompt title, "title to use for plots" Variable m,N=CountObjects("root:", 4 ) String used,list="\\M1:(:Unavailable Names;" for (m=0;m '%s'\r",fldrSav,GetDataFolder(1) if (d0>0) // for a valid d0, create the global Variable/G $(fldr+":d0")=d0 endif if (strlen(title)) String/G $(fldr+":title")=title endif return 0 End //======================================================================================= //======================================================================================= //Function aa() // print get_ranges("imagePath","EW5_") //End Static Function/T get_ranges(pathName,namePart) String pathName // probably 'imagePath' String namePart // name part of file = "EW5_" PathInfo $pathName String cmd, pathStr = ReplaceString(":", S_path, "/" ) pathStr = pathStr[strsearch(pathStr,"/",0),Inf] sprintf cmd, "do shell script \"ls \\\"%s\\\"\"",pathStr ExecuteScriptText cmd // print pathStr, " ",cmd String list = S_value Variable i=strlen(list) list = list[1,i-2] // remove leading and trailing double quote list = ReplaceString("\r", list, ";" )// change to a semicolon separated list // print list[0,71] Variable m,N = ItemsInList(list) Make/N=(N)/O/T fileList_get_ranges Wave/T fileList = fileList_get_ranges String name for (i=0,m=0;i1) StructGet/S/B=2 geo, strStruct // found structure information, load into geo else geo.xalfd = 0.15605 // nothing found, just use some reasonable looking default values geo.xbetd = 0.19635 geo.dd = 40.0143 geo.ddOffset = 0.0143 geo.NxCCD = 2084 geo.NyCCD = 2084 geo.dpsx = 50.01 geo.dpsy = 50.168 geo.xcent = 1089.7 geo.ycent = 942.901 // yc of Si calibration standard geo.xbet = 0.30943 geo.xgam = 0.75192 geo.wire.dia = 52 geo.wire.H0 = -5305.9 geo.wire.Hyc = -4889.03 geo.wire.F = 3830. endif GeometryUpdateCalc(geo) End // //Static Function FillGeometryStructDefault(geo) //fill the geometry structure with test values // STRUCT microGeometry &geo // geo.xalfd = 0.15605 // geo.xbetd = 0.19635 // geo.dd = 40.0143 // geo.ddOffset = 0.0143 // geo.NxCCD = 2084 // geo.NyCCD = 2084 // geo.dpsx = 50.01 // geo.dpsy = 50.168 // geo.xcent = 1089.7 // geo.ycent = 942.901 // yc of Si calibration standard // geo.xbet = 0.30943 // geo.xgam = 0.75192 // geo.wire.dia = 52 // geo.wire.H0 = -5305.9 // geo.wire.Hyc = -4889.03 // geo.wire.F = 3830. // GeometryUpdateCalc(geo) //End //======================================================================================= //======================================================================================= //=======================================================================================