#pragma rtGlobals=3 // Use modern global access method and strict wave access. ///////////////////////// // // Utility functions to: // calculate Q, Qx, Qy, Qz // fill the detector panels with simulated data (the model functions are here) // bin the 2D detector to 1D I(Q) based on Q and deltaQ (bin width) // ///////////////////////// // TODO: hard wired for a sphere - change this to allow minimal selections and altering of coefficients // TODO: add the "fake" 2D simulation to fill the panels which are then later averaged as I(Q) Function FillPanel_wModelData(det,qTot,type) Wave det,qTot String type SetDataFolder root:Packages:NIST:VSANS:VCALC:Front // q-values and detector arrays already allocated and calculated Duplicate/O det tmpInten,tmpSig,prob_i Variable imon,trans,thick,sdd,pixSizeX,pixSizeY,sdd_offset //imon = V_BeamIntensity()*CountTime imon = VCALC_getImon() //TODO: currently from the panel, not calculated trans = 0.8 thick = 0.1 // need SDD // need pixel dimensions // nominal sdd in meters, offset in mm, want result in cm ! sdd = VCALC_getSDD(type)*100 + VCALC_getTopBottomSDDOffset(type) / 10 // result is sdd in [cm] pixSizeX = VCALC_getPixSizeX(type) // cm pixSizeY = VCALC_getPixSizeY(type) //?? pick the function from a popup on the panel? (bypass the analysis panel, or maybe it's better to // keep the panel to keep people used to using it.) // peak @ 0.1 ~ AgBeh // Make/O/D coef_BroadPeak = {1e-9, 3, 20, 100.0, 0.1,3,0.1} // // peak @ 0.015 in middle of middle detector, maybe not "real" vycor, but that is to be resolved // Make/O/D coef_BroadPeak = {1e-9, 3, 20, 500.0, 0.015,3,0.1} String funcStr = VCALC_getModelFunctionStr() strswitch(funcStr) case "Big Debye": tmpInten = V_Debye(10,3000,0.0001,qTot[p][q]) break case "Big Sphere": tmpInten = V_SphereForm(1,900,1e-6,0.01,qTot[p][q]) break case "Debye": tmpInten = V_Debye(10,300,0.0001,qTot[p][q]) break case "Sphere": tmpInten = V_SphereForm(1,60,1e-6,0.001,qTot[p][q]) break case "AgBeh": tmpInten = V_BroadPeak(1e-9,3,20,100.0,0.1,3,0.1,qTot[p][q]) break case "Vycor": tmpInten = V_BroadPeak(1e-9,3,20,500.0,0.015,3,0.1,qTot[p][q]) break case "Empty Cell": tmpInten = V_EC_Empirical(2.2e-8,3.346,0.0065,9.0,0.016,qTot[p][q]) break case "Blocked Beam": tmpInten = V_BlockedBeam(1,qTot[p][q]) break default: tmpInten = V_Debye(10,300,0.1,qTot[p][q]) endswitch /////////////// // // calculate the scattering cross section simply to be able to estimate the transmission // Variable sig_sas=0 // // // remember that the random deviate is the coherent portion ONLY - the incoherent background is // // subtracted before the calculation. // CalculateRandomDeviate(funcUnsmeared,$coefStr,wavelength,"root:Packages:NIST:SAS:ran_dev",sig_sas) // // if(sig_sas > 100) // DoAlert 0,"SAS cross section > 100. Estimates of multiple scattering are unreliable. Choosing a model with a well-defined Rg may help" // endif // // // calculate the multiple scattering fraction for display (10/2009) // Variable ii,nMax=10,tau // mScat=0 // tau = thick*sig_sas // // this sums the normalized scattering P', so the result is the fraction of multiply coherently scattered // // neutrons out of those that were scattered // for(ii=2;ii 0.1) // Display warning // // Print "Sig_sas = ",sig_sas //////////////////// prob_i = trans*thick*pixSizeX*pixSizeY/(sdd)^2*tmpInten //probability of a neutron in q-bin(i) tmpInten = (imon)*prob_i //tmpInten is not the model calculation anymore!! /// **** can I safely assume a Gaussian error in the count rate?? tmpSig = sqrt(tmpInten) // corrected based on John's memo, from 8/9/99 tmpInten += gnoise(tmpSig) tmpInten = (tmpInten[p][q] < 0) ? 0 : tmpInten[p][q] // MAR 2013 -- is this the right thing to do tmpInten = trunc(tmpInten) det = tmpInten // if I want "absolute" scale -- then I lose the integer nature of the detector (but keep the random) // det /= trans*thick*pixSizeX*pixSizeY/(sdd)^2*imon KillWaves/Z tmpInten,tmpSig,prob_i SetDataFolder root: return(0) End // For a given detector panel, calculate the q-values // -work with everything as arrays // Input needed: // detector data // detector type (LRTB?) // beam center (may be off the detector) // SDD // lambda // // pixel dimensions for detector type (global constants) // - data dimensions read directly from array // // --What is calculated: // array of Q // array of qx,qy,qz // array of error already exists // // // -- sdd in meters // -- lambda in Angstroms Function V_Detector_2Q(data,qTot,qx,qy,qz,xCtr,yCtr,sdd,lam,pixSizeX,pixSizeY) Wave data,qTot,qx,qy,qz Variable xCtr,yCtr,sdd,lam,pixSizeX,pixSizeY // loop over the array and calculate the values - this is done as a wave assignment // TODO -- be sure that it's p,q -- or maybe p+1,q+1 as used in WriteQIS.ipf qTot = V_CalcQval(p,q,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) qx = V_CalcQX(p,q,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) qy = V_CalcQY(p,q,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) qz = V_CalcQZ(p,q,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) return(0) End ////////////////////// // NOTE: The Q calculations are different than what is in GaussUtils in that they take into // accout the different x/y pixel sizes and the beam center not being on the detector - // off a different edge for each LRTB type ///////////////////// //function to calculate the overall q-value, given all of the necesary trig inputs //and are in detector coordinates (1,128) rather than axis values //the pixel locations need not be integers, reals are ok inputs //sdd is in meters //wavelength is in Angstroms // //returned magnitude of Q is in 1/Angstroms // Function V_CalcQval(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) Variable xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY Variable dx,dy,qval,two_theta,dist sdd *=100 //convert to cm dx = (xaxval - xctr)*pixSizeX //delta x in cm dy = (yaxval - yctr)*pixSizeY //delta y in cm dist = sqrt(dx^2 + dy^2) two_theta = atan(dist/sdd) qval = 4*Pi/lam*sin(two_theta/2) return qval End //calculates just the q-value in the x-direction on the detector //input/output is the same as CalcQval() //ALL inputs are in detector coordinates // //sdd is in meters //wavelength is in Angstroms // // repaired incorrect qx and qy calculation 3 dec 08 SRK (Lionel and C. Dewhurst) // now properly accounts for qz // Function V_CalcQX(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) Variable xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY Variable qx,qval,phi,dx,dy,dist,two_theta qval = V_CalcQval(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) sdd *=100 //convert to cm dx = (xaxval - xctr)*pixSizeX //delta x in cm dy = (yaxval - yctr)*pixSizeY //delta y in cm phi = V_FindPhi(dx,dy) //get scattering angle to project onto flat detector => Qr = qval*cos(theta) dist = sqrt(dx^2 + dy^2) two_theta = atan(dist/sdd) qx = qval*cos(two_theta/2)*cos(phi) return qx End //calculates just the q-value in the y-direction on the detector //input/output is the same as CalcQval() //ALL inputs are in detector coordinates //sdd is in meters //wavelength is in Angstroms // // repaired incorrect qx and qy calculation 3 dec 08 SRK (Lionel and C. Dewhurst) // now properly accounts for qz // Function V_CalcQY(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) Variable xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY Variable dy,qval,dx,phi,qy,dist,two_theta qval = V_CalcQval(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) sdd *=100 //convert to cm dx = (xaxval - xctr)*pixSizeX //delta x in cm dy = (yaxval - yctr)*pixSizeY //delta y in cm phi = V_FindPhi(dx,dy) //get scattering angle to project onto flat detector => Qr = qval*cos(theta) dist = sqrt(dx^2 + dy^2) two_theta = atan(dist/sdd) qy = qval*cos(two_theta/2)*sin(phi) return qy End //calculates just the z-component of the q-vector, not measured on the detector //input/output is the same as CalcQval() //ALL inputs are in detector coordinates //sdd is in meters //wavelength is in Angstroms // // not actually used, but here for completeness if anyone asks // Function V_CalcQZ(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) Variable xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY Variable dy,qval,dx,phi,qz,dist,two_theta qval = V_CalcQval(xaxval,yaxval,xctr,yctr,sdd,lam,pixSizeX,pixSizeY) sdd *=100 //convert to cm //get scattering angle to project onto flat detector => Qr = qval*cos(theta) dx = (xaxval - xctr)*pixSizeX //delta x in cm dy = (yaxval - yctr)*pixSizeY //delta y in cm dist = sqrt(dx^2 + dy^2) two_theta = atan(dist/sdd) qz = qval*sin(two_theta/2) return qz End //phi is defined from +x axis, proceeding CCW around [0,2Pi] Threadsafe Function V_FindPhi(vx,vy) variable vx,vy variable phi phi = atan(vy/vx) //returns a value from -pi/2 to pi/2 // special cases if(vx==0 && vy > 0) return(pi/2) endif if(vx==0 && vy < 0) return(3*pi/2) endif if(vx >= 0 && vy == 0) return(0) endif if(vx < 0 && vy == 0) return(pi) endif if(vx > 0 && vy > 0) return(phi) endif if(vx < 0 && vy > 0) return(phi + pi) endif if(vx < 0 && vy < 0) return(phi + pi) endif if( vx > 0 && vy < 0) return(phi + 2*pi) endif return(phi) end Function V_SphereForm(scale,radius,delrho,bkg,x) Variable scale,radius,delrho,bkg Variable x // variables are: //[0] scale //[1] radius (A) //[2] delrho (A-2) //[3] background (cm-1) // Variable scale,radius,delrho,bkg // scale = w[0] // radius = w[1] // delrho = w[2] // bkg = w[3] // calculates scale * f^2/Vol where f=Vol*3*delrho*((sin(qr)-qrcos(qr))/qr^3 // and is rescaled to give [=] cm^-1 Variable bes,f,vol,f2 // //handle q==0 separately If(x==0) f = 4/3*pi*radius^3*delrho*delrho*scale*1e8 + bkg return(f) Endif // bes = 3*(sin(x*radius)-x*radius*cos(x*radius))/x^3/radius^3 bes = 3*sqrt(pi/(2*x*radius))*BesselJ(1.5,x*radius)/(x*radius) vol = 4*pi/3*radius^3 f = vol*bes*delrho // [=] A // normalize to single particle volume, convert to 1/cm f2 = f * f / vol * 1.0e8 // [=] 1/cm return (scale*f2+bkg) // Scale, then add in the background End Function V_Debye(scale,rg,bkg,x) Variable scale,rg,bkg Variable x // variables are: //[0] scale factor //[1] radius of gyration [A] //[2] background [cm-1] // calculates (scale*debye)+bkg Variable Pq,qr2 qr2=(x*rg)^2 Pq = 2*(exp(-(qr2))-1+qr2)/qr2^2 //scale Pq *= scale // then add in the background return (Pq+bkg) End // a sum of a power law and debye to approximate the scattering from a real empty cell // // make/O/D coef_ECEmp = {2.2e-8,3.346,0.0065,9.0,0.016} // Function V_EC_Empirical(aa,mm,scale,rg,bkg,x) Variable aa,mm,scale,rg,bkg Variable x // variables are: //[0] = A //[1] = power m //[2] scale factor //[3] radius of gyration [A] //[4] background [cm-1] Variable Iq // calculates (scale*debye)+bkg Variable Pq,qr2 // if(x*Rg < 1e-3) //added Oct 2008 to avoid numerical errors at low arg values // return(scale+bkg) // endif Iq = aa*x^-mm qr2=(x*rg)^2 Pq = 2*(exp(-(qr2))-1+qr2)/qr2^2 //scale Pq *= scale // then add the terms up return (Iq + Pq + bkg) End // blocked beam // Function V_BlockedBeam(bkg,x) Variable bkg Variable x return (bkg) End // // a broad peak to simulate silver behenate or vycor // // peak @ 0.1 ~ AgBeh // Make/O/D coef_BroadPeak = {1e-9, 3, 20, 100.0, 0.1,3,0.1} // // // peak @ 0.015 in middle of middle detector, maybe not "real" vycor, but that is to be resolved // Make/O/D coef_BroadPeak = {1e-9, 3, 20, 500.0, 0.015,3,0.1} // // Function V_BroadPeak(aa,nn,cc,LL,Qzero,mm,bgd,x) Variable aa,nn,cc,LL,Qzero,mm,bgd Variable x // variables are: //[0] Porod term scaling //[1] Porod exponent //[2] Lorentzian term scaling //[3] Lorentzian screening length [A] //[4] peak location [1/A] //[5] Lorentzian exponent //[6] background // local variables Variable inten, qval // x is the q-value for the calculation qval = x // do the calculation and return the function value inten = aa/(qval)^nn + cc/(1 + (abs(qval-Qzero)*LL)^mm) + bgd Return (inten) End Function SetDeltaQ(folderStr,type) String folderStr,type WAVE inten = $("root:Packages:NIST:VSANS:VCALC:" + folderStr + ":det_"+type) // 2D detector data Variable xDim,yDim,delQ xDim=DimSize(inten,0) yDim=DimSize(inten,1) if(xDim= 1) xDim=DimSize(inten,0) yDim=DimSize(inten,1) for(ii=0;ii= 2) xDim=DimSize(inten2,0) yDim=DimSize(inten2,1) for(ii=0;ii 0) // print val, nBin_qxqy[val] DeletePoints val, nq-val, iBin_qxqy,qBin_qxqy,nBin_qxqy,iBin2_qxqy,eBin_qxqy,eBin2D_qxqy if(val == 0) // all the points were deleted return(0) endif // since the beam center is not always on the detector, many of the low Q bins will have zero pixels // find the first non-zero point, working forwards val = -1 do val += 1 while(nBin_qxqy[val] == 0) DeletePoints 0, val, iBin_qxqy,qBin_qxqy,nBin_qxqy,iBin2_qxqy,eBin_qxqy,eBin2D_qxqy // ?? there still may be a point in the q-range that gets zero pixel contribution - so search this out and get rid of it val = numpnts(nBin_qxqy)-1 do if(nBin_qxqy[val] == 0) DeletePoints val, 1, iBin_qxqy,qBin_qxqy,nBin_qxqy,iBin2_qxqy,eBin_qxqy,eBin2D_qxqy endif val -= 1 while(val>0) SetDataFolder root: return(0) End