#include #include #include #include #include "globals.h" //#include "cufft.h" using namespace std; #define pi 3.14159 typedef complex scComplex; int cbessjyva(double v,complex z,double &vm,complex*cjv, complex*cyv,complex*cjvp,complex*cyvp); int bessjyv(double v,double x,double &vm,double *jv,double *yv, double *djv,double *dyv); complex Jl_neg(complex x) { //this function computes the bessel function of the first kind Jl(x) for l = -0.5 return ( sqrt(2.0/pi) * cos(x) )/sqrt(x); } double Jl_neg(double x) { //this function computes the bessel function of the first kind Jl(x) for l = -0.5 return ( sqrt(2.0/pi) * cos(x) )/sqrt(x); } double Yl_neg(double x) { //this function computes the bessel function of the second kind Yl(x) for l = -0.5; return ( sqrt(2.0/pi) * sin(x) )/sqrt(x); } void computeB(complex* B, float radius, complex refIndex, float lambda, int Nl) { double k = (2*pi)/lambda; int b = 2; //allocate space for the real bessel functions double* jv = (double*)malloc(sizeof(double)*(Nl+b)); double* yv = (double*)malloc(sizeof(double)*(Nl+b)); double* jvp = (double*)malloc(sizeof(double)*(Nl+b)); double* yvp = (double*)malloc(sizeof(double)*(Nl+b)); //allocate space for the complex bessel functions complex* cjv = (complex*)malloc(sizeof(complex)*(Nl+b)); complex* cyv = (complex*)malloc(sizeof(complex)*(Nl+b)); complex* cjvp = (complex*)malloc(sizeof(complex)*(Nl+b)); complex* cyvp = (complex*)malloc(sizeof(complex)*(Nl+b)); double kr = k*radius; complex knr = k*refIndex*(double)radius; complex n = refIndex; //compute the bessel functions for k*r double vm;// = Nl - 1; bessjyv((Nl)+0.5, kr, vm, jv, yv, jvp, yvp); //cout<<"Nl: "< scale_knr = sqrt(pi/(2.0*knr)); complex numer, denom; double j_kr; double y_kr; complex j_knr; complex j_d_knr; double j_d_kr; complex h_kr; complex h_d_kr; complex h_neg; complex h_pos; //cout<<"B coefficients:"<(j_kr, y_kr); //compute the derivatives if(l == 0) { //spherical bessel functions for l=0 j_d_kr = scale_kr * (Jl_neg(kr) - (jv[l] + kr*jv[l+1])/kr )/2.0; j_d_knr = scale_knr * ( Jl_neg(knr) - (cjv[l] + knr*cjv[l+1])/knr )/2.0; h_neg = complex(scale_kr*Jl_neg(kr), scale_kr*Yl_neg(kr)); h_pos = complex(scale_kr*jv[l+1], scale_kr*yv[l+1]); h_d_kr = (h_neg - (h_kr + kr*h_pos)/kr)/2.0; } else { //spherical bessel functions j_d_kr = scale_kr * (jv[l-1] - (jv[l] + kr*jv[l+1])/kr )/2.0; j_d_knr = scale_knr * ( cjv[l-1] - (cjv[l] + knr*cjv[l+1])/knr )/2.0; h_neg = complex(scale_kr*jv[l-1], scale_kr*yv[l-1]); h_pos = complex(scale_kr*jv[l+1], scale_kr*yv[l+1]); h_d_kr = (h_neg - (h_kr + kr*h_pos)/kr)/2.0; } numer = j_kr*j_d_knr*n - j_knr*j_d_kr; denom = j_knr*h_d_kr - h_kr*j_d_knr*n; complex temp = numer/denom; B[l] = scComplex(temp.real(), temp.imag()); //cout< integrateUi(float cAngleI, float cAngleO, float oAngleI, float oAngleO, float M = 2*pi) { /*This function integrates the incident field of magnitude M in the far zone in order to evaluate the field at the central pixel of a detector. cNAi = condenser inner angle cNAo = condenser outer angle oNAi = objective inner angle oNAo = objective outer angle M = field magnitude*/ float alphaIn = max(cAngleI, oAngleI); float alphaOut = min(cAngleO,oAngleO); complex Ui; if(alphaIn > alphaOut) Ui = complex(0.0, 0.0); else Ui = complex(M * 2 * pi * (cos(alphaIn) - cos(alphaOut)), 0.0f); return Ui; } void computeCondenserAlpha(float* alpha, int Nl, float cAngleI, float cAngleO) { /*This function computes the condenser integral in order to build the field of incident light alpha = list of Nl floating point values representing the condenser alpha as a function of l Nl = number of orders in the incident field cAngleI, cAngleO = inner and outer condenser angles (inner and outer NA)*/ //compute the Legendre polynomials for the condenser aperature float* PcNAo = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PcNAo, cos(cAngleO), Nl+1); float* PcNAi = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PcNAi, cos(cAngleI), Nl+1); for(int l=0; l integrateUs(float r, float lambda, complex eta, float cAngleI, float cAngleO, float oAngleI, float oAngleO, float M = 2*pi) { /*This function integrates the incident field of magnitude M in the far zone in order to evaluate the field at the central pixel of a detector. r = sphere radius lambda = wavelength eta = index of refraction cNAi = condenser inner NA cNAo = condenser outer NA oNAi = objective inner NA oNAo = objective outer NA M = field magnitude*/ //compute the required number of orders float k = 2*pi/lambda; int Nl = ceil( k + 4 * exp(log(k*r)/3) + 3 ); //compute the material coefficients B complex* B = (complex*)malloc(sizeof(complex)*Nl); //compute the Legendre polynomials for the condenser and objective aperatures float* PcNAo = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PcNAo, cos(cAngleO), Nl+1); float* PcNAi = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PcNAi, cos(cAngleI), Nl+1); float* PoNAo = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PoNAo, cos(oAngleO), Nl+1); float* PoNAi = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PoNAi, cos(oAngleI), Nl+1); //store the index of refraction; complex IR(eta.real(), eta.imag()); //compute the scattering coefficients computeB(B, r, IR, lambda, Nl); //aperature terms for the condenser (alpha) and objective (beta) float alpha; float beta; float c; complex Us(0.0, 0.0); for(int l=0; l Ui = integrateUi(cAngleI, cAngleO, oAngleI, oAngleO, 2*pi); float I0 = Ui.real() * Ui.real() + Ui.imag() * Ui.imag(); I0 *= scaleI0; float I; SpecPair temp; float nu; complex eta; complex Us, U; float vecLen = 0.0; for(int i=0; i(EtaN[i].A, EtaK[i].A); else eta = complex(baseIR, 0.0); //integrate the scattered field at the detector position Us = integrateUs(radius, lambda, eta, cAngleI, cAngleO, oAngleI, oAngleO, 2*pi); U = Us + Ui; float I = U.real() * U.real() + U.imag() * U.imag(); temp.nu = nu; //set the spectrum value based on the current display type if(dispSimType == AbsorbanceSpecType) temp.A = -log10(I/I0); else temp.A = I; if(dispNormalize) vecLen += temp.A * temp.A; SimSpectrum.push_back(temp); } vecLen = sqrt(vecLen); if(dispNormalize) for(int i=0; i sampleUs(complex* B, float* Alpha, int Nl, float r, float cAngleI, float cAngleO, float theta, float M = 2*pi) { /*This function takes a point sample of the scattered field in the far zone in order to evaluate the field at the central pixel of a detector. r = sphere radius lambda = wavelength eta = index of refraction cNAi = condenser inner NA cNAo = condenser outer NA theta = angle of the sample M = field magnitude*/ /* //compute the material coefficients B //complex* B = (complex*)malloc(sizeof(complex)*Nl); //compute the Legendre polynomials for theta (at the objective) float* Ptheta = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(Ptheta, cos(theta), Nl+1); //complex IR(eta.real(), eta.imag()); //aperature terms for the condenser (alpha) and objective (beta) float beta; float c; complex Us(0.0, 0.0); for(int l=0; l numer(0.0, -(l*pi)/2.0); Us += B[l] * exp(numer) * Ptheta[l] * Alpha[l] * pow(complex(0.0, 1.0), l); } //printf("Ptheta: %f\n", Ptheta[Nl-1]); return Us; }*/ /* void detectorSpectrum(int numSamples) { //integrate across the objective aperature and calculate the resulting intensity on a detector PD.StartTimer(SIMULATE_SPECTRUM); //clear the previous spectrum SimSpectrum.clear(); float dNu = 2.0f; float lambda; //compute the angles based on NA float cAngleI = asin(cNAi); float cAngleO = asin(cNAo); float oAngleI = asin(oNAi); float oAngleO = asin(oNAo); //implement a reflection-mode system if necessary if(opticsMode == ReflectionOpticsType){ //set the condenser to match the objective cAngleI = oAngleI; cAngleO = oAngleO; //invert the objective oAngleO = pi - cAngleI; oAngleI = pi - cAngleO; } //compute Nl (maximum order of the spectrum) //**************************************************************************** float maxNu = EtaK.back().nu; float maxLambda = 10000.0f/maxNu; float k = 2*pi/maxLambda; int Nl = ceil( k + 4 * exp(log(k*radius)/3) + 3 ); int nLambda = EtaK.size(); //compute alpha (condenser integral) //**************************************************************************** //compute the Legendre polynomials for the condenser aperature float* PcNAo = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PcNAo, cos(cAngleO), Nl+1); float* PcNAi = (float*)malloc(sizeof(float)*(Nl+1)); Legendre(PcNAi, cos(cAngleI), Nl+1); //allocate space for the alpha array float* alpha = (float*)malloc(sizeof(float)*(Nl + 1)); computeCondenserAlpha(alpha, Nl, cAngleI, cAngleO); for(int l=0; l Ui; Ui = integrateUi(cAngleI, cAngleO, oAngleI, oAngleO, 2*pi); I0 = Ui.real()*2*pi; float I; SpecPair temp; float nu; complex eta; complex Us, U; float vecLen = 0.0; for(int i=0; i(EtaN[i].A, EtaK[i].A); else eta = complex(baseIR, 0.0); //allocate memory for the scattering coefficients complex* B = (complex*)malloc(sizeof(complex)*Nl); complex IR(eta.real(), eta.imag()); computeB(B, radius, IR, lambda, Nl); //integrate the scattered field at the detector position I = 0.0; for(int iTheta = 0; iTheta < numSamples; iTheta++) { theta = oAngleI + iTheta * dTheta; Us = sampleUs(B, alpha, Nl, radius, cAngleI, cAngleO, theta, 2*pi); //calculate the intensity and add if(theta >= cAngleI && theta <= cAngleO) U = Us + 2*(float)pi; else U = Us; I += (U.real() * U.real() + U.imag() * U.imag()) * sin(theta) * 2 * pi * dTheta; } temp.nu = nu; if(i == 0) printf("I: %f I0: %f\n", I, I0); //set the spectrum value based on the current display type if(dispSimType == AbsorbanceSpecType) temp.A = -log10(I/I0); else temp.A = I; if(dispNormalize) vecLen += temp.A * temp.A; //temp.A = Us.real(); SimSpectrum.push_back(temp); free(B); } vecLen = sqrt(vecLen); if(dispNormalize) for(int i=0; i* B, int Nl, int nLambda) { float nu; complex eta; float* Lambda = (float*)malloc(sizeof(float) * nLambda); //for each wavenumber nu for(int i=0; i(EtaN[i].A, EtaK[i].A); else eta = complex(baseIR, 0.0); //allocate memory for the scattering coefficients //complex* B = (complex*)malloc(sizeof(complex)*Nl); complex IR(eta.real(), eta.imag()); computeB(&B[i * Nl], radius, IR, Lambda[i], Nl); } } void computeOpticalParameters(float& cAngleI, float& cAngleO, float& oAngleI, float& oAngleO, float& I0, float* alpha, int Nl) { computeCassegrainAngles(cAngleI, cAngleO, oAngleI, oAngleO); //evaluate the incident field intensity I0 = 0.0; float theta; complex Ui; Ui = integrateUi(cAngleI, cAngleO, oAngleI, oAngleO, 2*pi); I0 = Ui.real()*2*pi; //compute alpha (condenser integral) computeCondenserAlpha(alpha, Nl, cAngleI, cAngleO); } void gpuDetectorSpectrum(int numSamples) { //integrate across the objective aperature and calculate the resulting intensity on a detector PD.StartTimer(SIMULATE_SPECTRUM); //clear the previous spectrum SimSpectrum.clear(); //compute Nl (maximum order of the spectrum) int Nl = computeNl(); float* alpha = (float*)malloc(sizeof(float)*(Nl + 1)); float cAngleI, cAngleO, oAngleI, oAngleO, I0; computeOpticalParameters(cAngleI, cAngleO, oAngleI, oAngleO, I0, alpha, Nl); //allocate space for a list of wavelengths int nLambda = EtaK.size(); //allocate space for the 2D array (Nl x nu) of scattering coefficients complex* B = (complex*)malloc(sizeof(complex) * Nl * nLambda); computeBArray(B, Nl, nLambda); //allocate temporary space for the spectrum float* I = (float*)malloc(sizeof(float) * EtaK.size()); //compute the spectrum on the GPU PD.StartTimer(SIMULATE_GPU); cudaComputeSpectrum(I, (float*)B, alpha, Nl, nLambda, oAngleI, oAngleO, cAngleI, cAngleO, numSamples); PD.EndTimer(SIMULATE_GPU); updateSpectrum(I, I0, nLambda); PD.EndTimer(SIMULATE_SPECTRUM); } void SimulateSpectrum() { if(pointDetector) pointSpectrum(); else gpuDetectorSpectrum(objectiveSamples); //detectorSpectrum(objectiveSamples); } float absorbanceDistortion(){ //compute the mean of the spectrum float sumSim = 0.0; for(int i=0; i* B = (complex*)malloc(sizeof(complex) * Nl * nLambda); computeBArray(B, Nl, nLambda); float D; float e = 0.001; for(float i=0.0; i<=oNAo-step; i+=step) { for(float o=oNAi+step; o<=1.0; o+=step) { //set the current optical parameters cNAi = i; cNAo = o; //compute the optical parameters computeOpticalParameters(cAngleI, cAngleO, oAngleI, oAngleO, I0, alpha, Nl); //simulate the spectrum cudaComputeSpectrum(I, (float*)B, alpha, Nl, nLambda, oAngleI, oAngleO, cAngleI, cAngleO, objectiveSamples); updateSpectrum(I, I0, nLambda); if(dispSimType == AbsorbanceSpecType) { if(i + e >= o || i + e >= oNAo || oNAi + e >= o || oNAi + e >= oNAo) D = 0.0; else D = absorbanceDistortion(); } else { if(i >= o || oNAi >= oNAo) D=0; else D = intensityDistortion(); } outFile<