Skip to content

Commit 025918a

Browse files
committed
Corrected dependent display logic and warning message; Fixed polydisperse distribution
1 parent cf41552 commit 025918a

5 files changed

Lines changed: 209 additions & 179 deletions

File tree

src/app/calc/calculate.cpp

Lines changed: 85 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -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
350349
void 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
434422
void 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-

src/app/calc/calculate.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ class Calculate
3131
double CalculateForwardBackward(std::complex<double> *S1, std::complex<double> *S2,
3232
Parameters *para, unsigned int start, unsigned int end);
3333
double CalculateG(std::complex<double> *S1, std::complex<double> *S2, Parameters *para);
34-
bool CheckIndependentScattering(Parameters *para);
34+
bool CheckIndependentScattering(Parameters *para, double &clearanceToWavelength,
35+
double &sizeParameter, double &volFraction,
36+
double &criticalWavength, QString &strRegime);
3537
};
3638

3739
#endif // CALCULATE_H

0 commit comments

Comments
 (0)