@@ -152,7 +152,6 @@ void Calculate::DoSimulation(QLabel *progress, Parameters *para)
152152 delete[] sumS1;
153153 delete[] curS2;
154154 delete[] curS1;
155-
156155}
157156
158157// Compute Musp at reference wavelength
@@ -349,69 +348,60 @@ double Calculate::CalculateG(std::complex<double> *S1, std::complex<double> *S2,
349348// Set sphere parameters
350349void Calculate::SetSphereRadiusAndRefIndex (Parameters *para, unsigned int index, bool flagVolOrConc)
351350{
351+ const double volumeConst = 4.0 * M_PI / 3.0 ;
352+ const double sqrtTwoPi = sqrt (2.0 * M_PI);
353+ const double sig = para->stdDev ;
354+ const double twoSigSq = 2.0 * sig * sig;
355+
352356 if (para->nRadius == 1 ) // Mono Disperse
353- {
357+ {
358+ para->radArray [0 ] = para->meanRadius ;
354359 if (flagVolOrConc) // If volume fraction is selected, update number density
355360 {
356- double singleSphereVolume = 4.0 * M_PI * para->meanRadius * para-> meanRadius * para-> meanRadius / 3.0 ;
357- para->sphNumDensity = std::round (1e9 * para->volFraction /singleSphereVolume);
361+ double singleSphereVolume = volumeConst * pow ( para->meanRadius , 3 ) ;
362+ para->sphNumDensity = std::round (1e9 * para->volFraction / singleSphereVolume);
358363 }
359364
360- para->radArray [0 ] = para->meanRadius ;
361365 para->numDensityArray [0 ] = para->sphNumDensity ;
362366 para->scatRefRealArray [0 ] = para->scatRefReal ;
363367 para->scatRefImagArray [0 ] = para->scatRefImag ;
364368 para->medRefArray [0 ] = para->medRef ;
365369 }
366370 else // Poly Disperse
367- {
368- double temp, factor, stepR;
371+ {
369372 double totalSphereVolume = 0.0 ;
370373 double totalFuncSum = 0.0 ;
371- double *funcArray = new double [para->nRadius ];
374+ std::vector<double > funcArray (para->nRadius , 0.0 );
375+ double stepR = (para->maxRadius - para->minRadius ) / (para->nRadius - 1 );
376+
372377 for (unsigned int i = 0 ; i < para->nRadius ; i++)
373378 {
374- funcArray[i] = 0.0 ;
375- }
379+ double r = para-> minRadius + i * stepR ;
380+ para-> radArray [i] = r;
376381
377- stepR = (para->maxRadius - para->minRadius )/(para->nRadius -1 );
378-
379- switch (index)
380- {
381- case 0 : // Apply log normal distribution
382- for (unsigned int i=0 ; i<para->nRadius ; i++)
382+ if (index == 0 ) // Log-Normal
383383 {
384- para->radArray [i] = para->minRadius + i*stepR;
385- temp = log (para->radArray [i])-log (para->meanRadius );
386- funcArray[i] = (exp (-(temp*temp)/(2.0 * para->stdDev * para->stdDev )))/
387- (para->radArray [i] * para->stdDev * sqrt (2.0 * M_PI)) ;
388- double singleSphVolume = 4.0 * M_PI * para->radArray [i] *
389- para->radArray [i] * para->radArray [i] /3.0 ;
390- totalSphereVolume += funcArray[i] * singleSphVolume;
391- totalFuncSum += funcArray[i];
384+ double lnR = std::log (r);
385+ double lnMean = std::log (para->meanRadius );
386+ double diff = lnR - lnMean;
387+ funcArray[i] = (std::exp (-(diff * diff) / twoSigSq)) / (r * sig * sqrtTwoPi);
392388 }
393- break ;
394- case 1 : // Apply Gaussian distribution
395- for (unsigned int i=0 ; i<para->nRadius ; i++)
389+ else if (index == 1 ) // Gaussian
396390 {
397- para->radArray [i] = para->minRadius + i*stepR;
398- temp = para->radArray [i]-para->meanRadius ;
399- funcArray[i] = (exp (-(temp*temp/(2.0 * para->stdDev * para->stdDev ))))/
400- (para->stdDev * sqrt (2.0 * M_PI)) ;
401- double singleSphVolume = 4.0 * M_PI * para->radArray [i] *
402- para->radArray [i] * para->radArray [i] /3.0 ;
403- totalSphereVolume += funcArray[i] * singleSphVolume;
404- totalFuncSum += funcArray[i];
391+ double diff = r - para->meanRadius ;
392+ funcArray[i] = (std::exp (-(diff * diff) / twoSigSq)) / (sig * sqrtTwoPi);
405393 }
406- break ;
394+ totalSphereVolume += funcArray[i] * volumeConst * pow (r, 3 );
395+ totalFuncSum += funcArray[i];
407396 }
408397
409398 // Compute factor to compute number density
410- if (flagVolOrConc)
399+ double factor = 1e-12 ;
400+ if (flagVolOrConc && totalSphereVolume > 0 )
411401 {
412402 factor = 1e9 * para->volFraction /totalSphereVolume;
413403 }
414- else
404+ else if (totalFuncSum > 0 )
415405 {
416406 factor = para->sphNumDensity /totalFuncSum;
417407 }
@@ -423,160 +413,118 @@ void Calculate::SetSphereRadiusAndRefIndex(Parameters *para, unsigned int index,
423413 para->scatRefRealArray [i] = para->scatRefReal ;
424414 para->scatRefImagArray [i] = para->scatRefImag ;
425415 para->medRefArray [i] = para->medRef ;
426- }
427-
428- delete [] funcArray;
416+ }
429417 }
430418}
431419
432420// Selction of discrete sphere sizes for polydisperse distribution
433421// This process is used to obtain the best distribution for assigned mean diameter
434422void Calculate::DiameterRangeSetting (Parameters *para, unsigned int index)
435423{
436- double curR, dR;
437- double cutoffPercent = 0.0 ;
438- double mu, sigma = 0.0 ;
439- double modeR;
440- double curY, maxY, minY;
441- int i;
442-
443- if (index == 3 ) // Mono sphere Distribution
424+ // index: 0 = Log-normal, 1 = Gaussian, 3 = Monodisperse
425+ if (index == 3 )
444426 {
445427 para->minRadius = para->meanRadius ;
446428 para->maxRadius = para->meanRadius ;
429+ return ;
447430 }
448- else
449- {
450- // Calculate a percentage to cutoff points for Log normal and Gaussian distribution
451- if (para->nRadius <52 )
452- cutoffPercent = -0.0005 *para->nRadius + 0.031 ; // from 2:52 change from 3% to 0.5%
453- else
454- cutoffPercent = -0.00009 *para->nRadius + 0.00968 ; // from 52:102 change from 0.5% to 0.05%
455- sigma = para->stdDev ;
456- }
431+
432+ double mu, sigma;
433+ sigma = para->stdDev ;
434+
435+ // Define how much of the distribution tail to include.
436+ // A Z-score of 3.29 covers 99.9% of a Normal distribution.
437+ // Scale it based on nRadius if fewer bins require tighter bounds.
438+ double zScore = 3.0 + (para->nRadius / 100.0 );
457439
458440 switch (index)
459441 {
460- case 0 : // Apply log normal distribution. Source: http://en.wikipedia.org/wiki/Log-normal_distribution
461-
462- // find minR and maxR for a stand curve of mu=0 and multiply it by mean radius
463- mu = 0 ;
464- modeR = exp (mu - sigma*sigma);
465- maxY = (exp (-((log (modeR)-mu)*(log (modeR)-mu)/(2.0 * sigma * sigma))))/(modeR * sigma * sqrt (2.0 * M_PI));
466- minY = maxY*cutoffPercent;
467-
468- // backward search
469- i = 1 ;
470- dR = sigma*modeR/1e3 ;
471- do
442+ case 0 : // Log-normal Distribution
472443 {
473- curR = modeR - i*dR;
474- curY = (exp (-((log (curR)-mu)*(log (curR)-mu)/(2.0 * sigma * sigma))))/(curR * sigma * sqrt (2.0 * M_PI));
475- i++;
476- }
477- while (curY>minY);
478- para->minRadius = curR*para->meanRadius ;
479- if (para->minRadius <= 0.0 )
480- para->minRadius = 1e-10 ;
481-
482- // forward search
483- i = 1 ;
484- dR = sigma*modeR/1e3 ;
485- do
486- {
487- curR = modeR + i*dR;
488- curY = (exp (-((log (curR)-mu)*(log (curR)-mu)/(2.0 * sigma * sigma))))/(curR * sigma * sqrt (2.0 * M_PI));
489- i++;
490- }
491- while (curY>minY);
492- para->maxRadius = curR*para->meanRadius ;
493- break ;
444+ // If para->meanRadius is the Arithmetic Mean (E[X]),
445+ // convert it to the underlying Normal mu.
446+ // E[X] = exp(mu + sigma^2 / 2)
447+ mu = std::log (para->meanRadius ) - (sigma * sigma / 2.0 );
494448
495- case 1 : // Apply Gaussian distribution
449+ para->minRadius = std::exp (mu - zScore * sigma);
450+ para->maxRadius = std::exp (mu + zScore * sigma);
496451
497- mu = 0 ;
498- maxY = 1.0 /(sigma * sqrt ( 2.0 * M_PI)) ;
499- minY = maxY*cutoffPercent ;
500- dR = sigma/ 1e3 ;
452+ // Safety floor
453+ if (para-> minRadius < 1e-10 ) para-> minRadius = 1e-10 ;
454+ break ;
455+ }
501456
502- // forward search to find right end
503- i = 1 ;
504- do
457+ case 1 : // Gaussian (Normal) Distribution
505458 {
506- curR = mu + i*dR;
507- curY = (exp (-((curR-mu)*(curR-mu)/(2.0 * sigma * sigma))))/(sigma * sqrt (2.0 * M_PI));
508- i++;
459+ mu = para->meanRadius ;
460+
461+ para->minRadius = mu - zScore * sigma;
462+ para->maxRadius = mu + zScore * sigma;
463+
464+ // Physical constraint: Radius cannot be negative
465+ if (para->minRadius < 1e-10 ) para->minRadius = 1e-10 ;
466+ break ;
509467 }
510- while (curY>minY);
511- para->maxRadius = para->meanRadius + curR;
512- // Left end setting
513- if (curR < para->meanRadius )
514- para->minRadius = para->meanRadius - curR;
515- else
516- para->minRadius = 1e-10 ;
517- break ;
518468 }
519469}
520470
521471// Determines the scattering regime based on Tien et. al, A.R. Heat Trandfer 1(1987) & Galy et al. JQSRT 246(2020)
522- bool Calculate::CheckIndependentScattering (Parameters *para)
472+ bool Calculate::CheckIndependentScattering (Parameters *para, double &clearanceToWavelength, double &sizeParameter,
473+ double &volFraction, double &criticalWavength, QString &strRegime)
523474{
524- double volFraction = 0.0 ;
475+ double const volumeConstant = (4.0 /3.0 ) * M_PI ;
476+ double effectiveRadius = 0.0 ;
525477 double interParticleDistance;
526- double clearanceToWavelength;
527- double sizeParameter;
528- double wavelengthMicrons = para->startWavel / 1000.0 ;
478+
479+ // Calculate wavelengths in microns
480+ double criticalWavelength = para->endWavel / (para->medRef * 1000.0 );
481+
529482 if (para->nRadius == 1 ) // monodisperse
530483 {
531- double singleSphVolume = (4.0 /3.0 ) * M_PI * pow (para->meanRadius , 3 );
484+ effectiveRadius = para->meanRadius ;
485+ double singleSphVolume = volumeConstant * pow (effectiveRadius, 3 );
532486 volFraction = singleSphVolume * para->numDensityArray [0 ] / 1e9 ;
533487 interParticleDistance = 1e3 / pow (para->numDensityArray [0 ], 1.0 / 3.0 );
534- clearanceToWavelength = (interParticleDistance - 2 * para->meanRadius ) / wavelengthMicrons;
535- sizeParameter = 2.0 * M_PI * para->meanRadius / wavelengthMicrons;
536488 }
537- else
489+ else // polydisperse
538490 {
539491 double totalVolume = 0.0 ;
540492 double totalNumDensity = 0.0 ;
541493 for (unsigned int i = 0 ; i < para->nRadius ; i++)
542494 {
543- double singleSphVolume = ( 4.0 / 3.0 ) * M_PI * pow (para->radArray [i], 3 );
495+ double singleSphVolume = volumeConstant * pow (para->radArray [i], 3 );
544496 totalVolume += singleSphVolume * para->numDensityArray [i];
545497 totalNumDensity += para->numDensityArray [i];
546498 }
547499 volFraction = totalVolume / 1e9 ;
548500 interParticleDistance = 1e3 / pow (totalNumDensity, 1.0 / 3.0 );
549501
550502 double averageVolume = totalVolume / totalNumDensity;
551- double effectiveRadius = pow (3.0 * averageVolume / (4.0 * M_PI), 1.0 /3.0 );
552-
553- clearanceToWavelength = (interParticleDistance - 2 * effectiveRadius) / wavelengthMicrons;
554- sizeParameter = 2.0 * M_PI * effectiveRadius / wavelengthMicrons;
503+ effectiveRadius = pow (3.0 * averageVolume / (4.0 * M_PI), 1.0 /3.0 );
555504 }
556505
557- // Check dependent scattering, True: Dependent, False: Independent
506+ clearanceToWavelength = (interParticleDistance - 2 * effectiveRadius) / criticalWavelength;
507+ sizeParameter = 2.0 * M_PI * effectiveRadius / criticalWavelength;
508+
509+ // Determine the threshold for clearance based on the size parameter (Galy 2020)
558510 double requiredClearance = (sizeParameter <= 2.0 ) ? 2.0 : 5.0 ;
511+
559512 bool isDependent = false ;
560513
561- if (volFraction > 0.1 ) // High concentration regime
514+ if (volFraction > 0.1 ) // High concentration regime
562515 {
563- return true ;
516+ strRegime = " High Concentration Regime" ;
517+ isDependent = true ;
564518 }
565- if (volFraction > 0.006 ) // Transitional regime
519+ else if (volFraction > 0.006 ) // Transitional regime
566520 {
521+ strRegime = " Transitional Regime" ;
567522 isDependent = (clearanceToWavelength <= requiredClearance);
568523 }
569524 else // Low concentration regime
570525 {
571- if (sizeParameter > 0.388 )
572- {
573- isDependent = (clearanceToWavelength <= requiredClearance);
574- }
575- else
576- {
577- isDependent = false ;
578- }
526+ strRegime = " Low Concentration Regime" ;
527+ isDependent = (sizeParameter > 0.388 ) ? (clearanceToWavelength <= requiredClearance) : false ;
579528 }
580529 return isDependent;
581530}
582-
0 commit comments