#include #include #include #include #include "globals.h" #include //#include "cufft.h" using namespace std; #define pi 3.14159 typedef complex scComplex; extern int cbessjyva(double v,complex z,double &vm,complex*cjv, complex*cyv,complex*cjvp,complex*cyvp); extern 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, double radius, complex refIndex, double 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; B[l] = numer/denom; //B[l] = scComplex(temp.real(), temp.imag()); //cout< integrateUi(double cAngleI, double cAngleO, double oAngleI, double oAngleO, double 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*/ double alphaIn = max(cAngleI, oAngleI); double 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(double* alpha, int Nl, double cAngleI, double 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 double* PcNAo = (double*)malloc(sizeof(double)*(Nl+1)); Legendre(PcNAo, cos(cAngleO), Nl+1); double* PcNAi = (double*)malloc(sizeof(double)*(Nl+1)); Legendre(PcNAi, cos(cAngleI), Nl+1); for(int l=0; l integrateUs(double r, double lambda, complex eta, double cAngleI, double cAngleO, double oAngleI, double oAngleO, double 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 double k = 2*pi/lambda; int Nl = (int)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 double* PcNAo = (double*)malloc(sizeof(double)*(Nl+1)); Legendre(PcNAo, cos(cAngleO), Nl+1); double* PcNAi = (double*)malloc(sizeof(double)*(Nl+1)); Legendre(PcNAi, cos(cAngleI), Nl+1); double* PoNAo = (double*)malloc(sizeof(double)*(Nl+1)); Legendre(PoNAo, cos(oAngleO), Nl+1); double* PoNAi = (double*)malloc(sizeof(double)*(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) double alpha; double beta; double c; complex Us(0.0, 0.0); for(int l=0; l Ui = integrateUi(cAngleI, cAngleO, oAngleI, oAngleO, 2*pi); double I0 = Ui.real() * Ui.real() + Ui.imag() * Ui.imag(); I0 *= scaleI0; //double I; SpecPair temp; double nu; complex eta; complex Us, U; double vecLen = 0.0; for(unsigned 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; double 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(unsigned int i=0; i* B, int Nl, int nLambda) { double nu; complex eta; double* Lambda = (double*)malloc(sizeof(double) * nLambda); //for each wavenumber nu for(unsigned 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(double& cAngleI, double& cAngleO, double& oAngleI, double& oAngleO, double& I0, double* alpha, int Nl) { computeCassegrainAngles(cAngleI, cAngleO, oAngleI, oAngleO); //evaluate the incident field intensity I0 = 0.0; 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(); double* alpha = (double*)malloc(sizeof(double)*(Nl + 1)); double 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 double* I = (double*)malloc(sizeof(double) * EtaK.size()); //compute the spectrum on the GPU PD.StartTimer(SIMULATE_GPU); cudaComputeSpectrum(I, (double*)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); } double absorbanceDistortion(){ //compute the mean of the spectrum double sumSim = 0.0; for(unsigned int i=0; i* B = (complex*)malloc(sizeof(complex) * Nl * nLambda); computeBArray(B, Nl, nLambda); double D; double e = 0.001; for(double i=0.0; i<=oNAo-step; i+=step) { for(double 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, (double*)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<