orbitalthread.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                        orbitalthread.cpp  -  description
00003                              -------------------
00004     begin                : Sun Mar 27 2005
00005     copyright            : (C) 2005-2006 by Ben Swerts
00006     email                : bswerts@users.sourceforge.net
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00018 
00027 
00028 
00029 
00031 
00032 // C++ header files
00033 #include <cassert>
00034 #include <cfloat>
00035 #include <cmath>
00036 
00037 // Qt header files
00038 #include <qapplication.h>
00039 #include <qmutex.h>
00040 
00041 // Xbrabo header files
00042 #include "orbitalthread.h"
00043 
00047 
00049 OrbitalThread::OrbitalThread(QWidget* parentWidget, QMutex* sharedMutex, std::vector<Point3D<float> >* coordinates, 
00050                              const unsigned int type, const unsigned int atom, const unsigned int n, 
00051                              const unsigned int l, const int m, const float res, const float prob, 
00052                              const unsigned int dots) : QThread(),
00053   receiver(parentWidget),
00054   mutex(sharedMutex),
00055   coords(coordinates),
00056   atomNumber(atom),
00057   qnPrincipal(n),
00058   qnOrbital(l),
00059   qnMomentum(m),
00060   calculationType(type), 
00061   probability(prob),
00062   resolution(res),        
00063   numDots(dots),
00064   stopRequested(false)
00067 {
00068   assert(parentWidget != 0);
00069   assert(sharedMutex != 0);
00070   assert(coordinates != 0);
00071   assert(atom > 0);
00072   assert(prob >= 0.0f && prob <= 1.0f);
00073   assert(res > 1.0f);
00074   assert(n > l);
00075   assert(l >= static_cast<unsigned int>(abs(m))); // VC++ warns without the cast
00076   assert(type < 3); // < 5 if AccumulatedProbability and RadialPart work as required
00077 
00078   // get some statistics
00079   //qDebug("maximum float = %e (%d bits)", FLT_MAX, sizeof(float));
00080   //qDebug("maximum double = %e (%d bits)", DBL_MAX, sizeof(double));
00081   //qDebug("maximum long double = %Le (%d bits)", LDBL_MAX, sizeof(long double));
00082 
00086 }
00087 
00089 OrbitalThread::~OrbitalThread()
00091 {
00092 
00093 }
00094 
00096 void OrbitalThread::stop()
00098 {
00099   stopRequested = true;
00100 }
00101 
00103 double OrbitalThread::boundingSphereRadius()
00105 {
00106   return static_cast<double>(maximumRadius);
00107 }
00108 
00112 
00114 void OrbitalThread::run()
00117 {  
00119   unsigned int neededPrecision = PRECISION_UNKNOWN;
00120   if(calculationType != AngularPart)
00121   {
00123     // check whether the largest possible number ((4n)^n) can be represented by a float
00124     if(largestResult<float>(qnPrincipal, qnOrbital, qnMomentum, atomNumber) != HUGE_VAL)
00125     //if(powf(static_cast<float>(4 * atomNumber * qnPrincipal), qnPrincipal) != HUGE_VALF)
00126       neededPrecision = PRECISION_FLOAT;
00127     // by a double
00128     else if(largestResult<double>(qnPrincipal, qnOrbital, qnMomentum, atomNumber) != HUGE_VAL)
00129     //else if(pow(static_cast<double>(4 * atomNumber * qnPrincipal), qnPrincipal) != HUGE_VAL)
00130       neededPrecision = PRECISION_DOUBLE;
00131     // or by a long double
00132     else if(largestResult<long double>(qnPrincipal, qnOrbital, qnMomentum, atomNumber) != HUGE_VAL)
00133       neededPrecision = PRECISION_LONG_DOUBLE;
00134   }
00135   else
00136   {     
00138     // check whether the largest possible number ((2l)!) can be represented by a float
00139     if(factorial<float>(2*qnOrbital) != HUGE_VAL)
00140       neededPrecision = PRECISION_FLOAT;
00141     // by a double
00142     else if(factorial<float>(2*qnOrbital) != HUGE_VAL)
00143       neededPrecision = PRECISION_DOUBLE;
00144     // or by a long double
00145     else if(factorial<float>(2*qnOrbital) != HUGE_VAL)
00146       neededPrecision = PRECISION_LONG_DOUBLE;
00147   }
00148   qDebug("required precision = %d", neededPrecision);
00149   if(neededPrecision == PRECISION_UNKNOWN)
00150   {
00151   // notify the thread has ended
00152   QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1002));
00153   QApplication::postEvent(receiver, e);
00154     qDebug("exceeded long double limits");
00155     return;
00156   }
00157   if(neededPrecision != PRECISION_FLOAT)
00158   {
00159   // notify the thread has ended
00160   QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1002));
00161   QApplication::postEvent(receiver, e);
00162     qDebug("precision higher than float not implemented yet");
00163     return;
00164   }
00165 
00166   switch(calculationType)
00167   {
00168     case IsoProbability: 
00169             calcIsoProbability();
00170             break;
00171     case AccumulatedProbability: 
00172             calcAccumulatedProbability();
00173             break;
00174     case Density: 
00175             calcRandomDots();
00176             break;
00177     case RadialPart: 
00178             calcRadialPart();
00179             break;
00180     case AngularPart: 
00181             calcAngularPart();
00182             break;
00183   }
00184   // notify the thread has ended
00185   QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1002));
00186   QApplication::postEvent(receiver, e);
00187 }
00188 
00190 void OrbitalThread::calcIsoProbability()
00193 {
00194   const int updateFreq = static_cast<int>(resolution*resolution)/100;
00195 
00196   // clear the data
00197   mutex->lock();
00198   coords->clear();
00199   mutex->unlock();
00200   maximumRadius = 0.0f;
00201   std::vector<Point3D<float> > coordsList;
00202   coordsList.reserve(updateSize);
00203 
00204   // a short local form of the quantum numbers
00205   const unsigned int n = qnPrincipal;
00206   const unsigned int l = qnOrbital;
00207   const int m = qnMomentum;
00208 
00209   // values independent of r, theta or phi
00210   const float zna = static_cast<float>(atomNumber) / static_cast<float>(n) / abohr; // conversion factor for r->rho
00211   const float normR = pow(2.0f*zna, 1.5f) * sqrt(factorial<float>(n-l-1)/2.0f/n/factorial<float>(n+l)); // normalization factor for the radial part
00212   const float normTheta = pow(-1.0f, (m + abs(m))/2) * sqrtf( (2*l+1) * factorial<float>(l-abs(m)) / (2.0f * factorial<float>(l+abs(m)))); // normalization factor for the angular part (Theta dependent)
00213   const float normPhi = 1.0f / sqrtf(Point3D<float>::PI); // normalization factor for the angular part (Phi dependent)
00214   const float incTheta = 180.0f/resolution; // increment for theta
00215   const float incPhi = 360.0f/resolution; // increment for phi
00216   const float maxR = 2.0f*static_cast<float>(n*n); // maximum value for r to check
00217   const float incR = maxR / (10.0f * resolution); // increment for r
00218 
00220   for(float theta = 0.0f; theta < 180.0f; theta += incTheta) // rotation away from z-axis: only 180 degrees
00221   {
00223     for(float phi = 0.0f; phi < 360.0f; phi += incPhi) // rotation around z-axis: full 360 degrees
00224     {
00225       // notify the progress
00226       progress = static_cast<int>(theta/incTheta * resolution + phi/incPhi);
00227       if(progress % updateFreq == 0)
00228       {
00229         QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1001),&progress);
00230         QApplication::postEvent(receiver, e);
00231       }
00232                         
00233       // randomize theta within its square
00234       const float randTheta = theta + random(-incTheta/2.0f, incTheta/2.0f);
00235       // determine whether this point is to be drawn (|sin(theta)| function is maximum near the equator and zero at the poles)
00236       if(random(0.0f, 1.0f) > fabs(sinf(randTheta*Point3D<float>::DEGTORAD)))
00237         continue;
00238       // randomize phi within its square
00239       const float randPhi = phi + random(-incPhi/2.0f, incPhi/2.0f);
00240         
00241       // get the result for theta
00242       const float partTheta = normTheta * associatedLegendre(cosf(randTheta*Point3D<float>::DEGTORAD), abs(m), l);
00243       // get the result for phi
00244       float partPhi = normPhi;
00245       if(m == 0)
00246         partPhi /= sqrtf(2.0f);
00247       else if(m > 0)
00248        partPhi *= sinf(m*randPhi*Point3D<float>::DEGTORAD);
00249       else
00250        partPhi *= cosf(m*randPhi*Point3D<float>::DEGTORAD);
00251 
00252       // total angular result
00253       const float partY = partTheta * partPhi;
00254 
00256       float r = incR;
00257       float newProbability = 0.0f;
00258       const unsigned int maxPoints = l == 0 ? 2*n -1 : 2*(n - l); // the maximum number of times a value can be the given probability
00259       unsigned int numPoints = 0;
00260       // determine a starting oldProbability (for r = 0.0f)
00261       float oldProbability = pow(partY * normR * associatedLaguerre(0.0f, 2*l+1, n-l-1) , 2);
00262       // start the loop
00263       while(r < maxR && numPoints < maxPoints)
00264       {
00265         if(stopRequested)
00266           return;
00267         const float rho = 2.0f * zna * r;
00268         const float partR = normR * pow(rho, static_cast<int>(l)) * exp(-rho/2.0f) * associatedLaguerre(rho, 2*l+1, n-l-1);
00269 
00270         newProbability = partY * partR * partY * partR;
00271         //totalProbability += 4.0f * Point3D<float>::PI * newProbability * r*r / resolution;
00272 
00273         if(r > incR && ((oldProbability <= probability && newProbability >= probability) || (oldProbability >= probability && newProbability <= probability)))
00274         {
00275             // found a point
00276             const float minProb = oldProbability < newProbability ? oldProbability : newProbability;
00277             const float maxProb = oldProbability > newProbability ? oldProbability : newProbability;
00278             const float interpolatedR = r + incR * (probability - minProb)/(maxProb-minProb);
00279 
00280             //if(theta <= 180.0f/resolution && phi <= 360.0f/resolution)
00281             //  qDebug("point found at r = %f, interpolatedR = %f", r, interpolatedR);
00282 
00283             numPoints++;
00284             Point3D<float> newCoord;
00285             newCoord.setPolar(randTheta, randPhi, interpolatedR);
00286             newCoord.setID(partY*partR > 0.0f ? 1 : 0); // misuse Point3D's ID to store the phase (1 = pos, 0 = neg)
00287             coordsList.push_back(newCoord);
00288             updateList(coordsList);
00289             if(r > maximumRadius) maximumRadius = r;
00290         }
00291         oldProbability = newProbability;
00292         r += incR;
00293       }
00294       
00295       if(numPoints < maxPoints && oldProbability > probability)
00296       {
00297         //if(theta < 180.0f/resolution && phi < 360.0f/resolution)
00298         //  qDebug("trying remaining space");
00299 
00300         // the remaining cloud still has at least one point with the given probability
00301         while(true)
00302         {
00303           if(stopRequested)
00304             return;
00305           const float rho = 2.0f * zna * r;
00306           const float partR = normR * pow(rho, static_cast<int>(l)) * exp(-rho/2.0f) * associatedLaguerre(rho, 2*l+1, n-l-1);
00307           newProbability = partY * partR * partY * partR;
00308           if(oldProbability >= probability && newProbability <= probability)
00309           {
00310             const float interpolatedR = r + incR * (probability - newProbability)/(oldProbability-newProbability);
00311 
00312             //if(theta <= 180.0f/resolution && phi <= 360.0f/resolution)
00313             //  qDebug("extra point found at r = %f, interpolatedR = %f", r, interpolatedR);
00314 
00315             numPoints++;
00316             Point3D<float> newCoord;
00317             newCoord.setPolar(randTheta, randPhi, interpolatedR);
00318             newCoord.setID(partY*partR > 0.0f ? 1 : 0); 
00319             //mutex->lock();
00320             //coords->push_back(newCoord);
00321             //mutex->unlock();
00322             coordsList.push_back(newCoord);
00323             updateList(coordsList);
00324             if(r > maximumRadius) maximumRadius = r;
00325             break;
00326           }
00327           oldProbability = newProbability;
00328           r += incR;
00329         }
00330       }
00331     }    
00332   }
00333   updateList(coordsList, true);
00334 }
00335 
00337 void OrbitalThread::calcAccumulatedProbability()
00340 {
00341   const int updateFreq = static_cast<int>(resolution*resolution)/100;
00342 
00343   // clear the data
00344   mutex->lock();
00345   coords->clear();
00346   mutex->unlock();
00347   maximumRadius = 0.0f;
00348   std::vector<Point3D<float> > coordsList;
00349   coordsList.reserve(updateSize);
00350 
00351   // a short local form of the quantum numbers
00352   const unsigned int n = qnPrincipal;
00353   const unsigned int l = qnOrbital;
00354   const int m = qnMomentum;
00355   
00356   // values independent of r, theta or phi
00357   const float zna = static_cast<float>(atomNumber) / static_cast<float>(n) / abohr; // conversion factor for r->rho
00358   const float normR = pow(2.0f*zna, 1.5f) * sqrt(factorial<float>(n-l-1)/2.0f/n/factorial<float>(n+l)); // normalization factor for the radial part
00359   const float normTheta = pow(-1.0f, (m + abs(m))/2) * sqrtf( (2*l+1) * factorial<float>(l-abs(m)) / (2.0f * factorial<float>(l+abs(m)))); // normalization factor for the angular part (Theta dependent)
00360   const float normPhi = 1.0f / sqrtf(Point3D<float>::PI); // normalization factor for the angular part (Phi dependent)
00361   const float incTheta = 180.0f/resolution; // increment for theta
00362   const float incPhi = 360.0f/resolution; // increment for phi
00363   const float maxR = 2.0f*static_cast<float>(n*n); // maximum value for r to check
00364   const float incR = maxR / (10.0f * resolution); // increment for r
00365   
00367   for(float theta = 0.0f; theta < 180.0f; theta += incTheta) // rotation away from z-axis: only 180 degrees
00368   {
00369 
00371     for(float phi = 0.0f; phi < 360.0f; phi += incPhi) // rotation around z-axis: full 360 degrees
00372     {
00373       // notify the progress
00374       progress = static_cast<int>(theta/incTheta * resolution + phi/incPhi);
00375       if(progress % updateFreq == 0)
00376       {
00377         QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1001),&progress);
00378         QApplication::postEvent(receiver, e);
00379       }
00380 
00381       // randomize theta within its square
00382       const float randTheta = theta + random(-incTheta/2.0f, incTheta/2.0f);
00383       // determine whether this point is to be drawn (|sin(theta)| function is maximum near the equator and zero at the poles)
00384       if(random(0.0f, 1.0f) > fabs(sinf(randTheta*Point3D<float>::DEGTORAD)))
00385         continue;
00386       
00387       // get the result for theta
00388       const float partTheta = normTheta * associatedLegendre(cosf(randTheta*Point3D<float>::DEGTORAD), abs(m), l);
00389 
00390       // randomize phi within its square
00391       const float randPhi = phi + random(-incPhi/2.0f, incPhi/2.0f);
00392       // get the result for phi
00393       float partPhi = normPhi;
00394       if(m == 0)
00395         partPhi /= sqrtf(2.0f);
00396       else if(m > 0)
00397        partPhi *= sinf(m*randPhi*Point3D<float>::DEGTORAD);
00398       else
00399        partPhi *= cosf(m*randPhi*Point3D<float>::DEGTORAD);
00400 
00401       // total angular result
00402       const float partY = partTheta * partPhi;
00403 
00405       float r = 0.0f;
00406       float totalProbability = 0.0f;
00407       float amplitude = 0.0f;
00408       while (r < maxR)
00409       {
00410         if(stopRequested)
00411           return;
00412         const float rho = 2.0f * zna * r;
00413         const float partR = normR * pow(rho, static_cast<int>(l)) * exp(-rho/2.0f) * associatedLaguerre(rho, 2*l+1, n-l-1);
00414 
00415         // total amplitude
00416         amplitude = partY * partR;
00417         totalProbability += 4.0f * Point3D<float>::PI * amplitude*amplitude * r*r * incR;
00418 
00419         //qDebug("r = %f, amplitude = %f, totalProbability = %20.15f", r, amplitude, totalProbability);
00420         if(totalProbability >= probability)
00421           break;
00422 
00423         r += incR;
00424       }
00425       //break;
00426       // now r is the required radius
00427       //qDebug("getAccumProb: phi = %f, theta = %f, distance = %f", theta, phi, partY);
00428       Point3D<float> newCoord;
00429       newCoord.setPolar(randTheta, randPhi, r);
00430       newCoord.setID(amplitude > 0.0f ? 1 : 0); 
00431       coordsList.push_back(newCoord);
00432       updateList(coordsList);
00433       if(r > maximumRadius) maximumRadius = r;
00434     }
00435   }
00436   updateList(coordsList, true);
00437 }
00438 
00440 void OrbitalThread::calcRandomDots()
00442 {
00443   const int updateFreq = static_cast<int>(numDots)/100;
00444 
00445   // clear the data
00446   mutex->lock();
00447   coords->clear();
00448   mutex->unlock();
00449   maximumRadius = 0.0f;
00450   std::vector<Point3D<float> > coordsList;
00451   coordsList.reserve(updateSize);
00452 
00453   // a short local form of the quantum numbers
00454   const unsigned int n = qnPrincipal;
00455   const unsigned int l = qnOrbital;
00456   const int m = qnMomentum;
00457 
00458   // values independent of r, theta or phi
00459   const float zna = static_cast<float>(atomNumber) / static_cast<float>(n) / abohr; // conversion factor for r->rho
00460   const float normR = pow(2.0f*zna, 1.5f) * sqrt(factorial<float>(n-l-1)/2.0f/n/factorial<float>(n+l)); // normalization factor for the radial part
00461   const float normTheta = pow(-1.0f, (m + abs(m))/2) * sqrtf( (2*l+1) * factorial<float>(l-abs(m)) / (2.0f * factorial<float>(l+abs(m)))); // normalization factor for the angular part (Theta dependent)
00462   const float normPhi = 1.0f / sqrtf(Point3D<float>::PI); // normalization factor for the angular part (Phi dependent)
00463   const float maxR = 2.0f * static_cast<float>(n*n); // n*n is maximum radial probability for ns => this is the maximum radius for drawing points
00464   float maxRtest = 0.0f; // => this is the maximum radius needed for the maximum probability test (0 if l == 0)
00465   if(l == n-1)
00466     maxRtest = static_cast<float>(n*l); // correct (= l*(l+1))
00467   else if(l != 0)
00468     maxRtest = static_cast<float>(l*(l+1)); // maximum (higher n is smaller maxRtest)
00469 
00470   // run for 1/10th of the amount (1000 points max) to get an estimate on the maximum probability
00471   float maxProb = 0.0f;
00472   float maxProbR = 0.0f;
00473   unsigned int testLimit = numDots/10 < 1000 ? numDots/10 : 1000;
00474   for(unsigned int i = 0; i < testLimit; i++)
00475   {
00476     if(stopRequested)
00477       return;
00478     const float theta = random(0.0f, 180.0f);
00479     const float phi = random(0.0f, 360.0f);
00480     const float r = random(0.0f, maxRtest);
00481     // calculate the probability
00482     const float partTheta = normTheta * associatedLegendre(cosf(theta*Point3D<float>::DEGTORAD), abs(m), l);
00483     float partPhi = normPhi;
00484     if(m == 0)
00485       partPhi /= sqrtf(2.0f);
00486     else if(m > 0)
00487      partPhi *= sinf(m*phi*Point3D<float>::DEGTORAD);
00488     else
00489      partPhi *= cosf(m*phi*Point3D<float>::DEGTORAD);
00490     const float rho = 2.0f * zna * r;
00491     const float partR = normR * pow(rho, static_cast<int>(l)) * exp(-rho/2.0f) * associatedLaguerre(rho, 2*l+1, n-l-1);
00492     const float probability = pow(partTheta * partPhi * partR, 2);
00493     if(probability > maxProb)
00494     {
00495       maxProb = probability;
00496       maxProbR = r;
00497     }
00498   }
00499   qDebug("estimated maximum probability = %f", maxProb);
00500 
00501   // do the actual run
00502   unsigned int currentDots = 0;
00503   while(currentDots < numDots)
00504   {
00505     if(stopRequested)
00506       return;
00507 
00508     // generate a position in spherical coordinates
00509     const float theta = random(0.1f, 179.9f);
00510     const float phi = random(0.0f, 360.0f);
00511     const float r = random(0.0f, maxR);
00512 
00513     // determine whether this point is to be drawn (|sin(theta)| function is maximum near the equator and zero at the poles)
00514     if(random(0.0f, 1.0f) > fabs(sinf(theta*Point3D<float>::DEGTORAD)))
00515       continue;
00516 
00517     // calculate the probability
00518     const float partTheta = normTheta * associatedLegendre(cosf(theta*Point3D<float>::DEGTORAD), abs(m), l);
00519     float partPhi = normPhi;
00520     if(m == 0)
00521       partPhi /= sqrtf(2.0f);
00522     else if(m > 0)
00523      partPhi *= sinf(m*phi*Point3D<float>::DEGTORAD);
00524     else
00525      partPhi *= cosf(m*phi*Point3D<float>::DEGTORAD);
00526     const float rho = 2.0f * zna * r;
00527     const float partR = normR * pow(rho, static_cast<int>(l)) * exp(-rho/2.0f) * associatedLaguerre(rho, 2*l+1, n-l-1);
00528     const float probability = pow(partTheta * partPhi * partR, 2);
00529     if(probability > maxProb)
00530     {
00531       maxProb = probability; // adjust while in the loop
00532       maxProbR = r;
00533     }
00534     if(probability > random(0.0f, maxProb))
00535     {
00536       // add this point
00537       Point3D<float> newCoord;
00538       newCoord.setPolar(theta, phi, r);
00539       newCoord.setID(partTheta*partPhi*partR > 0.0f ? 1 : 0); 
00540       coordsList.push_back(newCoord);
00541       updateList(coordsList);
00542       if(r > maximumRadius) maximumRadius = r;
00543       currentDots++;
00544       // notify the progress
00545       if(currentDots % updateFreq == 0)
00546       {
00547         progress = currentDots;
00548         QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1001),&progress);
00549         QApplication::postEvent(receiver, e);
00550       }
00551     }
00552   }
00553   updateList(coordsList, true);
00554   qDebug("final maximum probability = %f", maxProb);
00555   qDebug(" corresponding radius = %f", maxProbR);
00556 }
00557 
00559 void OrbitalThread::calcRadialPart()
00561 {
00562   const int updateFreq = static_cast<int>(10.0f * resolution)/100;
00563 
00564   // clear the data
00565   mutex->lock();
00566   coords->clear();
00567   mutex->unlock();
00568   std::vector<Point3D<float> > coordsList;
00569   coordsList.reserve(updateSize);
00570   
00571   // a short local form of the quantum numbers
00572   const unsigned int n = qnPrincipal;
00573   const unsigned int l = qnOrbital;
00574 
00575   // values independent of r
00576   const float zna = static_cast<float>(atomNumber) / static_cast<float>(n) / abohr; // conversion factor for r->rho
00577   const float normR = pow(2.0f*zna, 1.5f) * sqrt(factorial<float>(n-l-1)/2.0f/n/factorial<float>(n+l)); // normalization factor for the radial part
00578   const float maxR = 2.0f*static_cast<float>(n*n); // maximum value for r to check
00579   const float incR = maxR / (10.0f * resolution); // increment for r
00580 
00581   maximumRadius = maxR;
00582 
00583   //float probability = 0.0f;
00584   //float probabilityRadius = 0.0f;
00585 
00586   // loop over r
00587   for(float r = 0.0f; r < maxR; r += incR)
00588   {
00589     if(stopRequested)
00590       return;
00591 
00592     // notify the progress
00593     progress = static_cast<unsigned int>(r/incR);
00594     if(progress % updateFreq == 0)
00595     {
00596       QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1001),&progress);
00597       QApplication::postEvent(receiver, e);
00598     }
00599 
00600     // calculate the value
00601     const float rho = 2.0f * zna * r;
00602     const float partR = normR * pow(rho, static_cast<int>(l)) * exp(-rho/2.0f) * associatedLaguerre(rho, 2*l+1, n-l-1);
00603 
00604     // the radial part => amplitude
00605     Point3D<float> newCoord1(r, partR, 0.0f);
00606     newCoord1.setID(1);
00607     // the radial part => probability = R^2r^2
00608     Point3D<float> newCoord2(r, partR*partR*r*r, 0.0f);
00609     newCoord2.setID(0);
00610     // add the points
00611     coordsList.push_back(newCoord1);
00612     coordsList.push_back(newCoord2);
00613     updateList(coordsList);
00614     /*// determine the distance with 90% probability
00615     if(probability < 0.90f)
00616     {
00617       probability += partr*partr*r*r/resolution;
00618       if(probability >= 0.90f)
00619         probabilityRadius = r;
00620     }*/
00621   }
00622   updateList(coordsList, true);
00623 }
00624 
00626 void OrbitalThread::calcAngularPart()
00628 {
00629   const int updateFreq = static_cast<int>(resolution * resolution)/100;
00630 
00631   // clear the data
00632   mutex->lock();
00633   coords->clear();
00634   mutex->unlock();
00635   maximumRadius = 0.0f;
00636   std::vector<Point3D<float> > coordsList;
00637   coordsList.reserve(updateSize);
00638   
00639   // a short local form of the quantum numbers
00640   const unsigned int l = qnOrbital;
00641   const int m = qnMomentum;
00642 
00643   // values independent of theta or phi
00644   const float normTheta = pow(-1.0f, (m + abs(m))/2) * sqrtf( (2*l+1) * factorial<float>(l-abs(m)) / (2.0f * factorial<float>(l+abs(m)))); // normalization factor for the angular part (Theta dependent)
00645   const float normPhi = 1.0f / sqrtf(Point3D<float>::PI); // normalization factor for the angular part (Phi dependent)
00646   const float incTheta = 180.0f/resolution; // increment for theta
00647   const float incPhi = 360.0f/resolution; // increment for phi
00648 
00650   for(float theta = 0.0f; theta < 180.0f; theta += incTheta) // rotation away from z-axis: only 180 degrees
00651   {
00652 
00654     for(float phi = 0.0f; phi < 360.0f; phi += incPhi) // rotation around z-axis: full 360 degrees
00655     {              
00656       if(stopRequested)
00657         return;
00658 
00659       // notify the progress
00660       progress = static_cast<int>(theta/incTheta * resolution + phi/incPhi);
00661       if(progress % updateFreq == 0)
00662       {
00663         QCustomEvent* e = new QCustomEvent(static_cast<QEvent::Type>(1001),&progress);
00664         QApplication::postEvent(receiver, e);
00665       }
00666 
00667       // randomize theta within its square
00668       const float randTheta = theta + random(-incTheta/2.0f, incTheta/2.0f);
00669       // determine whether this point is to be drawn (|sin(theta)| function is maximum near the equator and zero at the poles)
00670       if(random(0.0f, 1.0f) > fabs(sinf(randTheta*Point3D<float>::DEGTORAD)))      
00671         continue;
00672       // get the result for theta
00673       const float partTheta = normTheta * associatedLegendre(cosf(randTheta*Point3D<float>::DEGTORAD), abs(m), l);
00674 
00675       // randomize phi within its square
00676       const float randPhi = phi + random(-incPhi/2.0f, incPhi/2.0f);
00677       // get the result for phi
00678       float partPhi = normPhi;
00679       if(m == 0)
00680         partPhi /= sqrtf(2.0f);
00681       else if(m > 0)
00682        partPhi *= sinf(m*randPhi*Point3D<float>::DEGTORAD);
00683       else
00684        partPhi *= cosf(m*randPhi*Point3D<float>::DEGTORAD);
00685 
00686       // total angular result
00687       const float partY = partTheta * partPhi;
00688 
00689       // save the data
00690       const float r = fabs(partY);
00691       Point3D<float> newCoord;
00692       newCoord.setPolar(randTheta, randPhi, r);
00693       newCoord.setID(partY > 0.0f ? 1 : 0); 
00694       coordsList.push_back(newCoord);
00695       updateList(coordsList);
00696       if(r > maximumRadius) maximumRadius = r;
00697     }
00698   }
00699   updateList(coordsList, true);
00700 }
00701 
00703 void OrbitalThread::updateList(std::vector<Point3D<float> >& newCoords, bool final)
00706 {
00707   if(newCoords.size() >= updateSize || (final && !newCoords.empty()))
00708   {
00709     mutex->lock();
00710     coords->insert(coords->end(), newCoords.begin(), newCoords.end());
00711     mutex->unlock();
00712     newCoords.clear();
00713     qDebug("Added %d new points", updateSize);
00714   }
00715 }
00716 
00718 float OrbitalThread::associatedLegendre(const float x, const int m, const unsigned int l)
00720 {
00721   float prefactor = pow(1.0f - x*x, static_cast<float>(m)/2.0f) / pow(2.0f, static_cast<int>(l));
00722   int upperBound = 0;
00723   if(((l-m) % 2) == 0) // l-m is even
00724     upperBound = (l-m)/2;
00725   else // l-m is odd
00726     upperBound = (l-m-1)/2;
00727 
00728   float result = 0.0f;
00729   for(int j = 0; j <= upperBound; j++)
00730   {
00731     float tempvar = pow(-1.0f, j)*factorial<float>(2*l-2*j);
00732     tempvar /= factorial<float>(j)*factorial<float>(l-j)*factorial<float>(l-abs(m)-2*j);
00733     tempvar *= pow(x, static_cast<int>(l-m-2*j));
00734     result +=  tempvar;
00735   }
00736   return prefactor*result;
00737 }
00738 
00740 float OrbitalThread::associatedLaguerre(const float x, const int m, const unsigned int n)
00742 {  
00743   // from MathWorld: Rodrigues representation of the Laguerre polynomial (Arfken & Webers definition)
00744   if(n == 0)
00745     return 1.0f;
00746   float result = 0.0f;
00747   for(unsigned int j = 0; j <= n; j++)
00748     result += pow(-1.0f, static_cast<int>(j)) * factorial<float>(n+m) / factorial<float>(n-j) / factorial<float>(m+j) / factorial<float>(j) * pow(x, static_cast<int>(j));
00749   return result;
00750 }
00751 
00753 template <class T> T OrbitalThread::factorial(const unsigned int number)
00758 {
00759   if(number <= 1)
00760     return 1;
00761   unsigned int counter = number - 1;
00762   T result = static_cast<T>(number);
00763   while(counter > 1)
00764   {
00765     result *= static_cast<T>(counter);
00766     counter--;
00767   }
00768   return result;
00769 }
00770 
00772 float OrbitalThread::random(const float min, const float max)
00774 {  
00775   return min + static_cast<float>(rand())/RAND_MAX*(max - min);
00776 }
00777 
00779 template <class T> T OrbitalThread::largestResult(const unsigned int n, const unsigned int l, const int m, const unsigned int Z)
00782 {
00783   T partPhi = static_cast<T>(0.5641895835); // 1/sqrt(PI) // is partial specialization for local variables possible
00784   T partTheta = sqrt(factorial<T>(2*l+1) * factorial<T>(l-abs(m)) / 2.0 / factorial<T>(l+abs(m))) * associatedLegendre(1.0f, abs(m), l);
00785   T rmax = static_cast<T>(3*n*n);
00786   T partR = pow(2.0*Z/n/abohr, 1.5) * sqrt(factorial<T>(n-l-1)/2.0/n/factorial<T>(n+l)) * pow(2.0*Z/n/abohr*rmax, static_cast<int>(l)) * associatedLaguerre(2.0*Z/n/abohr*rmax, 2*l+1, n-l-1);
00787   return partPhi*partTheta*partR;
00788 }
00789 
00793 
00794 const float OrbitalThread::abohr = 1.0f;
00795 const unsigned int OrbitalThread::updateSize = 1000;
00796 

Generated on Fri May 19 14:31:55 2006 for Brabosphere by  doxygen 1.4.6-NO