51 m_TAG =
"ThermalModelCanonical";
54 m_InteractionModel =
Ideal;
92 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
116 if (computeFluctuations) {
155 m_QuantumStats = stats;
156 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
157 m_TPS->Particle(i).UseStatistics(stats);
177 bool cB = m_ConstrainMuB && !
m_BCE;
178 bool cQ = m_ConstrainMuQ && !
m_QCE;
179 bool cS = m_ConstrainMuS && !
m_SCE;
180 bool cC = m_ConstrainMuC && !
m_CCE;
182 if (!cB && !cQ && !cS && !cC) {
187 if (resetInitialValues) {
203 if (resetInitialValues)
218 if (m_ConstrainGammaC) {
219 m_Parameters.gammaC = 1.0;
222 m_ConstrainMuB &= !
m_BCE;
223 m_ConstrainMuQ &= !
m_QCE;
224 m_ConstrainMuS &= !
m_SCE;
225 m_ConstrainMuC &= !
m_CCE;
244 m_ConstrainMuB &= !
m_BCE;
245 m_ConstrainMuQ &= !
m_QCE;
246 m_ConstrainMuS &= !
m_SCE;
247 m_ConstrainMuC &= !
m_CCE;
262 assert(m_IGFExtraConfig.MagneticField.B == 0.);
264 m_FluctuationsCalculated =
false;
271 EnforceSaddlePointLOForNonIdeal();
276 ComputeAnalyticCumulants(
false);
289 m_Parameters.muB = 0.0;
290 m_Parameters.muQ = 0.0;
291 m_Parameters.muS = 0.0;
292 m_Parameters.muC = 0.0;
297 m_Parameters.muB = 0.0;
299 m_Parameters.muQ = 0.0;
301 m_Parameters.muS = 0.0;
303 m_Parameters.muC = 0.0;
310 for (
size_t i = 0; i < m_densities.size(); ++i) {
322 if (ind <
static_cast<int>(
m_Corr.size()))
325 cout <<
"ThermalModelCanonical::CalculatePrimordialDensities: Warning! No canonical partition function for this particle!" << endl;
332 if (ind <
static_cast<int>(
m_Corr.size()))
352 double totB = CalculateBaryonDensity() * m_Parameters.SVc;
353 if (fabs(m_Parameters.B - totB) > TOL) {
355 sprintf(cc,
"**WARNING** ThermalModelCanonical: Inaccurate calculation of total baryon number.\
359\n", m_Parameters.B, totB);
363 m_ValidityLog.append(cc);
365 m_LastCalculationSuccessFlag =
false;
371 double totQ = CalculateChargeDensity() * m_Parameters.SVc;
372 if (fabs(m_Parameters.Q - totQ) > TOL) {
373 sprintf(cc,
"**WARNING** ThermalModelCanonical: Inaccurate calculation of total electric charge.\
377\n", m_Parameters.Q, totQ);
381 m_ValidityLog.append(cc);
383 m_LastCalculationSuccessFlag =
false;
389 double totS = CalculateStrangenessDensity() * m_Parameters.SVc;
390 if (fabs(m_Parameters.S - totS) > TOL) {
391 sprintf(cc,
"**WARNING** ThermalModelCanonical: Inaccurate calculation of total strangeness.\
395\n", m_Parameters.S, totS);
399 m_ValidityLog.append(cc);
401 m_LastCalculationSuccessFlag =
false;
407 double totC = CalculateCharmDensity() * m_Parameters.SVc;
408 if (fabs(m_Parameters.C - totC) > TOL) {
409 sprintf(cc,
"**WARNING** ThermalModelCanonical: Inaccurate calculation of total charm.\
413\n", m_Parameters.C, totC);
417 m_ValidityLog.append(cc);
419 m_LastCalculationSuccessFlag =
false;
435 EnforceSaddlePointLOForNonIdeal();
440 Vc = m_Parameters.SVc;
446 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
447 int i2 = m_TPS->PdgToId(-m_TPS->Particle(i).PdgId());
449 if (std::abs(m_Chem[i] - m_Chem[i2]) > 1.e-8) {
450 throw std::runtime_error(
"ThermalModelCanonical::CalculatePartitionFunctions: Partial chemical equilibrium canonical ensemble only supported if particle-antiparticle fugacities are symmetric!");
458 ComputeSaddlePointPartitionFunctions();
461 for (
size_t iN = 0; iN <
m_PartialZ.size(); ++iN) {
488 bool AllMuZero =
true;
489 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
501 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
507 throw std::invalid_argument(
"ThermalModelCanonical: neutral particle cannot have non-zero ce charges");
509 if (ind <
static_cast<int>(Nsx.size()))
516 if (ind <
static_cast<int>(Nsx.size())) {
519 Nsx[ind] += tdens * cosh(m_Chem[i] / m_Parameters.T);
520 Nsy[ind] += tdens * sinh(m_Chem[i] / m_Parameters.T);
532 if (ind <
static_cast<int>(Nsx.size())) {
535 Nsx[ind] += tdens * cosh(n * m_Chem[i] / m_Parameters.T);
536 Nsy[ind] += tdens * sinh(n * m_Chem[i] / m_Parameters.T);
548 for (
int i = 0; i < static_cast<int>(Nsx.size()); ++i) {
557 int nmax = max(3, (
int)sqrt((
double)(m_Parameters.B*m_Parameters.B + m_Parameters.Q*m_Parameters.Q + m_Parameters.S*m_Parameters.S + m_Parameters.C*m_Parameters.C)));
558 if (m_Parameters.B == 0 && m_Parameters.Q == 0 && m_Parameters.S == 0 && m_Parameters.C == 0)
566 for (
size_t i = 0; i <
m_PartialZ.size(); ++i) {
578 for (
int iB = 0; iB < maxB; ++iB) {
580 vector<double> xlegB, wlegB;
583 double aB = iB * dphiB;
584 if (iB >= nmax) aB =
xMath::Pi() - (iB + 1) * dphiB;
585 double bB = aB + dphiB;
601 for (
int iS = 0; iS < maxS; ++iS) {
602 vector<double> xlegS, wlegS;
605 double aS = iS * dphiS;
606 if (iS >= nmax) aS =
xMath::Pi() - (iS + 1) * dphiS;
607 double bS = aS + dphiS;
622 for (
int iQ = 0; iQ < maxQ; ++iQ) {
623 vector<double> xlegQ, wlegQ;
626 double aQ = iQ * dphiQ;
627 double bQ = aQ + dphiQ;
642 for (
int iC = 0; iC < maxC; ++iC) {
643 vector<double> xlegC, wlegC;
645 double aC = iC * dphiC;
646 if (iC >= nmax) aC =
xMath::Pi() - (iC + 1) * dphiC;
647 double bC = aC + dphiC;
657 for (
size_t iBt = 0; iBt < xlegB.size(); ++iBt) {
658 for (
size_t iSt = 0; iSt < xlegS.size(); ++iSt) {
659 for (
size_t iQt = 0; iQt < xlegQ.size(); ++iQt) {
660 for (
size_t iCt = 0; iCt < xlegC.size(); ++iCt) {
662 double wx = 0., wy = 0., mx = 0., my = 0.;
663 for (
size_t i = 0; i <
m_PartialZ.size(); ++i) {
670 cosph[i] = cos(tS*xlegS[iSt] + tQ * xlegQ[iQt] + tC * xlegC[iCt]);
671 sinph[i] = sin(tS*xlegS[iSt] + tQ * xlegQ[iQt] + tC * xlegC[iCt]);
674 wx += Nsx[i] * cosph[i];
675 wy += Nsx[i] * sinph[i];
678 mx += Nsx[i] * (cosph[i] - 1.);
682 cosph[i] = cos(tB*xlegB[iBt] + tS * xlegS[iSt] + tQ * xlegQ[iQt] + tC * xlegC[iCt]);
683 mx += Nsx[i] * (cosph[i] - 1.);
686 sinph[i] = sin(tB*xlegB[iBt] + tS * xlegS[iSt] + tQ * xlegQ[iQt] + tC * xlegC[iCt]);
687 my += Nsy[i] * sinph[i];
696 wmod = sqrt(wx*wx + wy * wy);
697 warg = atan2(wy, wx);
700 for (
size_t iN = 0; iN <
m_PartialZ.size(); ++iN) {
701 int tBg = m_Parameters.B -
m_QNvec[iN].B;
702 int tQg = m_Parameters.Q -
m_QNvec[iN].Q;
703 int tSg = m_Parameters.S -
m_QNvec[iN].S;
704 int tCg = m_Parameters.C -
m_QNvec[iN].C;
708 m_PartialZ[iN] += wlegB[iBt] * wlegS[iSt] * wlegQ[iQt] * wlegC[iCt] *
709 cos(tBg * xlegB[iBt] + tSg * xlegS[iSt] + tQg * xlegQ[iQt] + tCg * xlegC[iCt] - tBg * warg) *
715 m_PartialZ[iN] += wlegB[iBt] * wlegS[iSt] * wlegQ[iQt] * wlegC[iCt] * cos(tBg*xlegB[iBt] + tSg * xlegS[iSt] + tQg * xlegQ[iQt] + tCg * xlegC[iCt]) * exp(mx);
717 m_PartialZ[iN] += wlegB[iBt] * wlegS[iSt] * wlegQ[iQt] * wlegC[iCt] * exp(mx)
718 * (cos(tBg*xlegB[iBt] + tSg * xlegS[iSt] + tQg * xlegQ[iQt] + tCg * xlegC[iCt]) * cos(my)
719 + sin(tBg*xlegB[iBt] + tSg * xlegS[iSt] + tQg * xlegQ[iQt] + tCg * xlegC[iCt]) * sin(my));
734 for (
size_t iN = 0; iN <
m_PartialZ.size(); ++iN) {
750 for (
size_t iN = 0; iN <
m_PartialZ.size(); ++iN) {
764 double ret1 = 0., ret2 = 0., ret3 = 0.;
767 return tpart.
ScaledVariance(m_Parameters, m_UseWidth, m_Chem[part]);
776 if (ind <
static_cast<int>(
m_Corr.size()) && ind2 <
static_cast<int>(
m_Corr.size()))
779 if (ind <
static_cast<int>(
m_Corr.size()))
783 double ret1num = 0., ret1zn = 0.;
789 if (ind <
static_cast<int>(
m_Corr.size())) {
790 ret1num +=
m_Corr[ind] * n * densityClusterN;
791 ret1zn +=
m_Corr[ind] * densityClusterN;
797 if (ind <
static_cast<int>(
m_Corr.size()) && ind2 <
static_cast<int>(
m_Corr.size()))
806 ret1 = ret1num / ret1zn;
807 ret2 = ret2 / ret1zn;
808 ret3 = -ret1zn * m_Parameters.SVc;
810 return ret1 + ret2 + ret3;
814 int NN = m_densities.size();
816 vector<double> yld(NN, 0);
817 vector<double> ret1num(NN, 0);
818 vector< vector<double> > ret2num(NN, vector<double>(NN, 0.));
820 for (
int i = 0; i < NN; ++i)
821 yld[i] = m_densities[i] * m_Parameters.SVc;
823 for (
int i = 0; i < NN; ++i) {
826 ret1num[i] = tpart.
ScaledVariance(m_Parameters, m_UseWidth, m_Chem[i]) * yld[i];
839 if (ind <
static_cast<int>(
m_Corr.size()))
840 ret1num[i] +=
m_Corr[ind] * n * densityClusterN * m_Parameters.SVc;
845 for (
int i = 0; i < NN; ++i) {
846 for (
int j = 0; j < NN; ++j) {
859 ret2num[i][j] = yld[i] * yld[j];
862 for (
int n1 = 1; n1 <= n1max; ++n1) {
863 for (
int n2 = 1; n2 <= n2max; ++n2) {
873 if (ind <
static_cast<int>(
m_Corr.size()))
874 ret2num[i][j] +=
m_Corr[ind] * densityClusterN1 * densityClusterN2 * m_Parameters.SVc * m_Parameters.SVc;
882 m_PrimCorrel.resize(NN);
883 for (
int i = 0; i < NN; ++i)
884 m_PrimCorrel[i].resize(NN);
885 m_TotalCorrel = m_PrimCorrel;
887 for (
int i = 0; i < NN; ++i) {
888 for (
int j = 0; j < NN; ++j) {
889 m_PrimCorrel[i][j] = 0.;
891 m_PrimCorrel[i][j] += ret1num[i] / yld[i];
892 m_PrimCorrel[i][j] += ret2num[i][j] / yld[i];
893 m_PrimCorrel[i][j] += -yld[j];
894 m_PrimCorrel[i][j] *= yld[i] / m_Parameters.SVc / m_Parameters.T;
896 m_PrimCorrel[i][j] = 0.;
900 for (
int i = 0; i < NN; ++i) {
901 m_wprim[i] = m_PrimCorrel[i][i];
902 if (m_densities[i] > 0.) m_wprim[i] *= m_Parameters.T / m_densities[i];
903 else m_wprim[i] = 1.;
904 if (m_wprim[i] != m_wprim[i]) m_wprim[i] = 1.;
907 m_TwoParticleCorrelationsCalculated =
true;
911 EnforceSaddlePointLOForNonIdeal();
916 ComputeAnalyticCumulants(
true);
933 m_FluctuationsCalculated =
true;
935 for (
size_t i = 0; i < m_wprim.size(); ++i) {
955 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
965 if (ind <
static_cast<int>(
m_Corr.size()))
971 if (ind <
static_cast<int>(
m_Corr.size()))
993 for (
int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
1003 if (ind <
static_cast<int>(
m_Corr.size()))
1010 if (ind <
static_cast<int>(
m_Corr.size()))
1034 ret += -m_Parameters.muB / m_Parameters.T * m_Parameters.B / m_Parameters.SVc;
1036 ret += -m_Parameters.muB * CalculateBaryonDensity() / m_Parameters.T;
1039 ret += -m_Parameters.muQ / m_Parameters.T * m_Parameters.Q / m_Parameters.SVc;
1041 ret += -m_Parameters.muQ * CalculateChargeDensity() / m_Parameters.T;
1044 ret += -m_Parameters.muS / m_Parameters.T * m_Parameters.S / m_Parameters.SVc;
1046 ret += -m_Parameters.muS * CalculateStrangenessDensity() / m_Parameters.T;
1049 ret += -m_Parameters.muC / m_Parameters.T * m_Parameters.C / m_Parameters.SVc;
1051 ret += -m_Parameters.muC * CalculateCharmDensity() / m_Parameters.T;
1078 return (
m_BCE != 0);
1080 return (
m_QCE != 0);
1082 return (
m_SCE != 0);
1084 return (
m_CCE != 0);
1100 void ThermalModelCanonical::PrepareModelGCE()
1108 m_Parameters.muB = 0.0;
1111 m_Parameters.muS = m_Parameters.muB / 3.;
1114 m_Parameters.muQ = -m_Parameters.muB / 30.;
1119 m_Parameters.muC = 0.;
1122 m_Parameters.muB, m_Parameters.muQ, m_Parameters.muS, m_Parameters.muC,
1123 static_cast<bool>(
m_BCE),
1124 static_cast<bool>(
m_QCE),
1125 static_cast<bool>(
m_SCE),
1126 static_cast<bool>(
m_CCE));
1134 void ThermalModelCanonical::CleanModelGCE()
1143 m_InteractionModel = model->InteractionModel();
1145 m_InteractionModel =
Ideal;
1149 void ThermalModelCanonical::EnforceSaddlePointLOForNonIdeal()
1157 std::cerr <<
"**WARNING** " << m_TAG
1158 <<
": Non-ideal interaction model is only supported with the SaddlePointLO method. "
1159 <<
"Falling back to SaddlePointLO." << std::endl;
1168 void ThermalModelCanonical::SolveSaddlePointEquations()
1183 int N = m_TPS->ComponentsNumber();
1185 for (
int j = 0; j < N; ++j) {
1196 m_Parameters.muB = 0.0;
1198 m_Parameters.muQ = 0.0;
1200 m_Parameters.muS = 0.0;
1202 m_Parameters.muC = 0.0;
1207 void ThermalModelCanonical::SetupSaddlePointChargeIndices()
1217 double ThermalModelCanonical::ComputeSigmaLogDetFromModel()
1223 int Nparts = m_TPS->ComponentsNumber();
1224 double Vc = m_Parameters.SVc;
1225 double T = m_Parameters.T;
1230 Eigen::MatrixXd Sigma = Eigen::MatrixXd::Zero(d, d);
1231 for (
int j = 0; j < Nparts; ++j) {
1232 for (
int k = 0; k < Nparts; ++k) {
1233 if (pc[j][k] == 0.0)
continue;
1234 double chi_jk = pc[j][k] * Vc * T;
1235 for (
int ia = 0; ia < d; ++ia) {
1237 double q_aj = m_TPS->Particle(j).GetCharge(a);
1238 if (q_aj == 0.0)
continue;
1239 for (
int ib = 0; ib < d; ++ib) {
1241 Sigma(ia, ib) += q_aj * m_TPS->Particle(k).GetCharge(b) * chi_jk;
1249 Eigen::LLT<Eigen::MatrixXd> llt(Sigma);
1250 if (llt.info() == Eigen::Success) {
1252 for (
int i = 0; i < d; ++i)
1253 logDet += 2.0 * log(llt.matrixL()(i, i));
1255 double det = Sigma.determinant();
1256 logDet = (det > 0.0) ? log(det) : -1.e100;
1262 void ThermalModelCanonical::ComputeSaddlePointPartitionFunctions()
1267 SolveSaddlePointEquations();
1270 SetupSaddlePointChargeIndices();
1271 m_modelgce->CalculateTwoParticleCorrelations();
1280 static_cast<double>(m_Parameters.B),
1281 static_cast<double>(m_Parameters.Q),
1282 static_cast<double>(m_Parameters.S),
1283 static_cast<double>(m_Parameters.C)
1286 int Nparts = m_TPS->ComponentsNumber();
1287 double Vc = m_Parameters.SVc;
1288 double T = m_Parameters.T;
1295 double term1 = p0 * Vc / T;
1297 for (
int ia = 0; ia < d; ++ia) {
1312 double nCharmRef = 0.0;
1315 for (
int j = 0; j < Nparts; ++j) {
1316 int absC = abs(m_TPS->Particle(j).Charm());
1318 nCharmRef += absC * dens[j];
1324 double muDotN_0 = 0.0;
1325 for (
int ia = 0; ia < d; ++ia) {
1332 int zeroIdx =
m_QNMap[QuantumNumbers(0, 0, 0, 0)];
1334 for (
size_t iN = 0; iN <
m_PartialZ.size(); ++iN) {
1335 if (
static_cast<int>(iN) == zeroIdx) {
1343 double Nshifted[4] = { Nref[0], Nref[1], Nref[2], Nref[3] };
1344 for (
int ia = 0; ia < d; ++ia) {
1346 Nshifted[a] -= charges[a];
1352 double muCguess_shifted = muCguess;
1353 if (
m_CCE && nCharmRef > 0.0 && Nshifted[3] != 0.0) {
1354 double arg = Nshifted[3] / (2.0 * nCharmRef * m_Parameters.V);
1355 muCguess_shifted = m_Parameters.T * asinh(arg);
1362 double muBinit = (charges[0] != 0) ? muBguess :
m_SaddlePointMu[0] * m_Parameters.T;
1363 double muQinit = (charges[1] != 0) ? muQguess :
m_SaddlePointMu[1] * m_Parameters.T;
1364 double muSinit = (charges[2] != 0) ? muSguess :
m_SaddlePointMu[2] * m_Parameters.T;
1365 double muCinit = (charges[3] != 0) ? muCguess_shifted :
m_SaddlePointMu[3] * m_Parameters.T;
1371 bool converged =
m_modelgce->SolveChemicalPotentials(
1372 Nshifted[0], Nshifted[1], Nshifted[2], Nshifted[3],
1373 muBinit, muQinit, muSinit, muCinit,
1374 static_cast<bool>(
m_BCE),
static_cast<bool>(
m_QCE),
1375 static_cast<bool>(
m_SCE),
static_cast<bool>(
m_CCE));
1379 printf(
"**WARNING** ThermalModelCanonical::ComputeSaddlePointPartitionFunctions: "
1380 "Solver did not converge for QN sector (B,Q,S,C) = (%.0f,%.0f,%.0f,%.0f). Skipping.\n",
1381 Nshifted[0], Nshifted[1], Nshifted[2], Nshifted[3]);
1386 double muOverT_c[4];
1387 muOverT_c[0] =
m_modelgce->Parameters().muB / T;
1388 muOverT_c[1] =
m_modelgce->Parameters().muQ / T;
1389 muOverT_c[2] =
m_modelgce->Parameters().muS / T;
1390 muOverT_c[3] =
m_modelgce->Parameters().muC / T;
1393 bool muValid =
true;
1394 for (
int ia = 0; ia < 4; ++ia) {
1395 if (!std::isfinite(muOverT_c[ia])) {
1401 printf(
"**WARNING** ThermalModelCanonical::ComputeSaddlePointPartitionFunctions: "
1402 "NaN/inf chemical potentials for QN sector (B,Q,S,C) = (%.0f,%.0f,%.0f,%.0f). Skipping.\n",
1403 Nshifted[0], Nshifted[1], Nshifted[2], Nshifted[3]);
1411 if (charges[0] != 0) muBguess =
m_modelgce->Parameters().muB;
1412 if (charges[1] != 0) muQguess =
m_modelgce->Parameters().muQ;
1413 if (charges[2] != 0) muSguess =
m_modelgce->Parameters().muS;
1414 if (charges[3] != 0) muCguess =
m_modelgce->Parameters().muC;
1416 std::vector<double> muStar_c(Nparts);
1417 for (
int j = 0; j < Nparts; ++j) {
1418 muStar_c[j] =
m_modelgce->ChemicalPotential(j);
1422 double p_c =
m_modelgce->CalculatePressure();
1423 double deltaP = Vc * (p_c - p0) / T;
1426 double muDotN_c = 0.0;
1427 for (
int ia = 0; ia < d; ++ia) {
1429 muDotN_c += muOverT_c[a] * Nshifted[a];
1431 double deltaMuN = muDotN_c - muDotN_0;
1435 m_modelgce->CalculateTwoParticleCorrelations();
1436 double logDetSigma_c = ComputeSigmaLogDetFromModel();
1437 double deltaLogDet = logDetSigma_c - logDetSigma_0;
1440 double lnR = deltaP - deltaMuN - 0.5 * deltaLogDet;
1443 if (!std::isfinite(lnR)) {
1454 void ThermalModelCanonical::ComputeAnalyticCumulants(
bool computeCovariance)
1469 SolveSaddlePointEquations();
1471 m_modelgce->CalculateTwoParticleCorrelations();
1478 SetupSaddlePointChargeIndices();
1480 int Nparts = m_TPS->ComponentsNumber();
1481 double Vc = m_Parameters.SVc;
1482 double T = m_Parameters.T;
1486 const auto& modelDensities =
m_modelgce->Densities();
1489 double epsGCE =
m_modelgce->CalculateEnergyDensity();
1490 double pGCE =
m_modelgce->CalculatePressure();
1494 std::vector<double> W1(Nparts, 0.0);
1495 std::vector<double> W2(Nparts, 0.0);
1496 for (
int j = 0; j < Nparts; ++j) {
1499 W1[j] = modelDensities[j] * Vc;
1500 W2[j] = pc[j][j] * Vc * T;
1508 auto buildSigma = [&](
const std::vector<int>& idx) -> Eigen::MatrixXd {
1509 int dd =
static_cast<int>(idx.size());
1510 Eigen::MatrixXd S = Eigen::MatrixXd::Zero(dd, dd);
1511 for (
int j = 0; j < Nparts; ++j) {
1512 for (
int k = 0; k < Nparts; ++k) {
1513 if (pc[j][k] == 0.0)
continue;
1514 double chi_jk = pc[j][k] * Vc * T;
1515 for (
int ia = 0; ia < dd; ++ia) {
1516 double q_aj = m_TPS->Particle(j).GetCharge(idx[ia]);
1517 if (q_aj == 0.0)
continue;
1518 for (
int ib = 0; ib < dd; ++ib)
1519 S(ia, ib) += q_aj * m_TPS->Particle(k).GetCharge(idx[ib]) * chi_jk;
1534 std::vector<int> kept;
1535 for (
size_t c = 0; c < cand.size(); ++c) {
1536 std::vector<int> trial = kept;
1537 trial.push_back(cand[c]);
1538 Eigen::MatrixXd St = buildSigma(trial);
1539 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(St, Eigen::EigenvaluesOnly);
1540 double maxEv = es.eigenvalues()(trial.size() - 1);
1541 double minEv = es.eigenvalues()(0);
1542 if (maxEv > 0.0 && minEv > 1.e-10 * maxEv)
1545 std::cerr <<
"**WARNING** " << m_TAG
1546 <<
": dropping linearly dependent conserved-charge axis "
1547 << cand[c] <<
" (B=0,Q=1,S=2,C=3) from the saddle-point basis." << std::endl;
1557 double logDetSigma = 0.0;
1559 Eigen::LLT<Eigen::MatrixXd> llt(Sigma);
1560 if (llt.info() == Eigen::Success) {
1561 for (
int i = 0; i < d; ++i)
1562 logDetSigma += 2.0 * log(llt.matrixL()(i, i));
1564 double det = Sigma.determinant();
1565 logDetSigma = (det > 0.0) ? log(det) : -1.e100;
1573 double term1 = pGCE * Vc / T;
1575 for (
int ia = 0; ia < d; ++ia) {
1577 double Na = (a == 0) ? m_Parameters.B : (a == 1) ? m_Parameters.Q : (a == 2) ? m_Parameters.S : m_Parameters.C;
1580 double term3 = -0.5 * d * log(2.0 *
xMath::Pi()) - 0.5 * logDetSigma;
1593 std::vector<double> W3(Nparts, 0.0);
1594 std::vector<double> W4(Nparts, 0.0);
1595 std::vector<double> Ej(Nparts, 0.0);
1598 for (
int j = 0; j < Nparts; ++j) {
1599 ThermalParticle& tpart = m_TPS->Particle(j);
1604 if (tpart.Statistics() == 0
1608 double omega = W2[j];
1615 for (
int n = 1; n <= tpart.ClusterExpansionOrder(); ++n) {
1617 double omega_n = dens_n * Vc /
static_cast<double>(n);
1618 W3[j] += n * n * n * omega_n;
1619 W4[j] += n * n * n * n * omega_n;
1622 Ej[j] += n * n * edens_n;
1638 bool isIdealGas = (m_InteractionModel ==
Ideal);
1640 Eigen::MatrixXd SigmaInv = Sigma.inverse();
1642 std::vector<double> P(Nparts * d, 0.0);
1643 std::vector<double> Gjj(Nparts, 0.0);
1646 for (
int j = 0; j < Nparts; ++j) {
1647 for (
int ia = 0; ia < d; ++ia) {
1649 for (
int ib = 0; ib < d; ++ib) {
1651 val += SigmaInv(ia, ib) * m_TPS->Particle(j).GetCharge(b);
1653 P[j * d + ia] = val;
1657 for (
int j = 0; j < Nparts; ++j) {
1659 for (
int ia = 0; ia < d; ++ia) {
1661 g += m_TPS->Particle(j).GetCharge(a) * P[j * d + ia];
1668 auto computeG = [&](
int j,
int k) ->
double {
1670 for (
int ia = 0; ia < d; ++ia) {
1672 g += m_TPS->Particle(j).GetCharge(a) * P[k * d + ia];
1689 double deltaEps = 0.0;
1690 for (
int j = 0; j < Nparts; ++j) {
1691 if (Ej[j] == 0.0 || Gjj[j] == 0.0)
1693 deltaEps += Ej[j] * Gjj[j];
1706 for (
int l = 0; l < Nparts; ++l) {
1708 m_densities[l] = modelDensities[l];
1713 double mean = W1[l];
1717 double nloTerm1 = -0.5 * W3[l] * Gjj[l];
1719 double sumGjjW3Gjl = 0.0;
1720 for (
int j = 0; j < Nparts; ++j) {
1721 if (W3[j] == 0.0 || Gjj[j] == 0.0)
1723 double G_jl = computeG(j, l);
1724 sumGjjW3Gjl += W3[j] * Gjj[j] * G_jl;
1726 double nloTerm2 = 0.5 * W2[l] * sumGjjW3Gjl;
1728 mean += nloTerm1 + nloTerm2;
1731 m_densities[l] = mean / Vc;
1734 if (!computeCovariance) {
1742 m_PrimCorrel.resize(NN);
1743 for (
int i = 0; i < NN; ++i)
1744 m_PrimCorrel[i].resize(NN, 0.0);
1745 m_TotalCorrel = m_PrimCorrel;
1753 for (
int l = 0; l < NN; ++l) {
1755 m_PrimCorrel[l][l] = pc[l][l];
1775 for (
int l = 0; l < NN; ++l) {
1779 for (
int m = l; m < NN; ++m) {
1783 double cov = -W2[l] * W2[m] * computeG(l, m);
1791 std::vector<double> Ml(d * d, 0.0);
1792 std::vector<double> Mm(d * d, 0.0);
1794 for (
int j = 0; j < Nparts; ++j) {
1798 double D_jl = (j == l ? 1.0 : 0.0) - W2[l] * computeG(j, l);
1799 double D_jm = (j == m ? 1.0 : 0.0) - W2[m] * computeG(j, m);
1801 double w3Djl = W3[j] * D_jl;
1802 double w3Djm = W3[j] * D_jm;
1804 for (
int ic = 0; ic < d; ++ic) {
1805 double Pjc = P[j * d + ic];
1806 for (
int ia = 0; ia < d; ++ia) {
1808 double q_aj = m_TPS->Particle(j).GetCharge(a);
1809 Ml[ic * d + ia] += w3Djl * Pjc * q_aj;
1810 Mm[ic * d + ia] += w3Djm * Pjc * q_aj;
1816 for (
int ic = 0; ic < d; ++ic)
1817 for (
int ia = 0; ia < d; ++ia)
1818 C_lm += Ml[ic * d + ia] * Mm[ia * d + ic];
1821 std::vector<double> S_blm(d, 0.0);
1822 for (
int k = 0; k < Nparts; ++k) {
1825 double D_kl = (k == l ? 1.0 : 0.0) - W2[l] * computeG(k, l);
1826 double D_km = (k == m ? 1.0 : 0.0) - W2[m] * computeG(k, m);
1827 double w3DklDkm = W3[k] * D_kl * D_km;
1828 for (
int ib = 0; ib < d; ++ib) {
1830 S_blm[ib] += m_TPS->Particle(k).GetCharge(b) * w3DklDkm;
1834 std::vector<double> SinvS(d, 0.0);
1835 for (
int ia = 0; ia < d; ++ia) {
1836 for (
int ib = 0; ib < d; ++ib)
1837 SinvS[ia] += SigmaInv(ia, ib) * S_blm[ib];
1841 for (
int j = 0; j < Nparts; ++j) {
1845 double D_jl = (j == l ? 1.0 : 0.0) - W2[l] * computeG(j, l);
1846 double D_jm = (j == m ? 1.0 : 0.0) - W2[m] * computeG(j, m);
1848 double term1 = W4[j] * D_jl * D_jm;
1850 double d2alpha_j = 0.0;
1851 for (
int ia = 0; ia < d; ++ia) {
1853 d2alpha_j -= m_TPS->Particle(j).GetCharge(a) * SinvS[ia];
1855 double term2 = W3[j] * d2alpha_j;
1857 B_lm += Gjj[j] * (term1 + term2);
1860 cov += -0.5 * B_lm + 0.5 * C_lm;
1863 double susc = cov / (Vc * T);
1864 m_PrimCorrel[l][m] = susc;
1866 m_PrimCorrel[m][l] = susc;
1881 std::vector<std::vector<double>> vl(Nparts, std::vector<double>(d, 0.0));
1882 std::vector<std::vector<double>> ul(Nparts, std::vector<double>(d, 0.0));
1883 for (
int l = 0; l < Nparts; ++l) {
1884 for (
int j = 0; j < Nparts; ++j) {
1885 if (pc[j][l] == 0.0)
1887 double chi_jl = pc[j][l] * Vc * T;
1888 for (
int ia = 0; ia < d; ++ia) {
1890 vl[l][ia] += m_TPS->Particle(j).GetCharge(a) * chi_jl;
1893 for (
int ia = 0; ia < d; ++ia) {
1894 for (
int ib = 0; ib < d; ++ib)
1895 ul[l][ia] += SigmaInv(ia, ib) * vl[l][ib];
1903 for (
int l = 0; l < NN; ++l) {
1904 for (
int m = l; m < NN; ++m) {
1905 double cov = pc[l][m] * Vc * T;
1906 for (
int ia = 0; ia < d; ++ia)
1907 cov -= vl[l][ia] * ul[m][ia];
1909 double susc = cov / (Vc * T);
1910 m_PrimCorrel[l][m] = susc;
1912 m_PrimCorrel[m][l] = susc;
1918 for (
int i = 0; i < NN; ++i) {
1919 m_wprim[i] = m_PrimCorrel[i][i];
1920 if (m_densities[i] > 0.)
1921 m_wprim[i] *= T / m_densities[i];
1924 if (m_wprim[i] != m_wprim[i])
1928 m_TwoParticleCorrelationsCalculated =
true;
map< string, double > params
virtual void CalculateSusceptibilityMatrix()
Calculates the conserved charges susceptibility matrix.
virtual void FixParametersNoReset()
Method which actually implements ConstrainChemicalPotentialsNoReset() (for backward compatibility).
void SetSoverB(double SB)
The entropy per baryon ratio to be used to constrain the baryon chemical potential.
virtual void ChangeTPS(ThermalParticleSystem *TPS)
Change the particle list.
virtual void CalculateParticleChargeCorrelationMatrix()
Calculates the matrix of correlators between primordial (and also final) particle numbers and conserv...
bool UsePartialChemicalEquilibrium()
Whether partial chemical equilibrium with additional chemical potentials is used.
void SetQoverB(double QB)
The electric-to-baryon charge ratio to be used to constrain the electric chemical potential.
virtual bool SolveChemicalPotentials(double totB=0., double totQ=0., double totS=0., double totC=0., double muBinit=0., double muQinit=0., double muSinit=0., double muCinit=0., bool ConstrMuB=true, bool ConstrMuQ=true, bool ConstrMuS=true, bool ConstrMuC=true)
The procedure which calculates the chemical potentials which reproduce the specified total baryon,...
bool ConstrainMuS() const
bool ConstrainMuQ() const
virtual void FillChemicalPotentials()
Sets the chemical potentials of all particles.
virtual void FixParameters()
Method which actually implements ConstrainChemicalPotentials() (for backward compatibility).
bool ConstrainMuC() const
void SetUseWidth(bool useWidth)
Sets whether finite resonance widths are used. Deprecated.
virtual void SetParameters(const ThermalModelParameters ¶ms)
The thermal parameters.
virtual void SetElectricChemicalPotential(double muQ)
Set the electric chemical potential.
virtual void ValidateCalculation()
Checks whether issues have occured during the calculation of particle densities in the CalculateDensi...
virtual void CalculateProxySusceptibilityMatrix()
Calculates the susceptibility matrix of conserved charges proxies.
ThermalModelBase(ThermalParticleSystem *TPS, const ThermalModelParameters ¶ms=ThermalModelParameters())
Construct a new ThermalModelBase object.
virtual void SetStrangenessChemicalPotential(double muS)
Set the strangeness chemical potential.
virtual void CalculateDensities()
Calculates the primordial and total (after decays) densities of all species.
const ThermalModelParameters & Parameters() const
virtual void CalculateTwoParticleFluctuationsDecays()
Computes particle number correlations and fluctuations for all final-state particles which are marked...
bool ConstrainMuB() const
std::map< QuantumNumbers, int > m_QNMap
Maps QuantumNumbers combinations to a 1-dimensional index.
double m_MultExpBanalyt
Exponential multiplier for analytical baryon fugacity calculations.
int m_IntegrationIterationsMultiplier
A multiplier to increase the number of iterations during the numerical integration used to calculate ...
int m_BMAX
Maximum baryon number.
int m_SaddlePointDim
Saddle-point approximation data (for SaddlePoint quadrature method)
virtual void SetStatistics(bool stats)
std::vector< double > m_PartialZ
The computed canonical partition function.
int m_QCE
Flag indicating if electric charge is conserved canonically.
ThermalModelBase * m_modelgce_ext
External GCE model (not owned), set via SetModelGCE()
void SolveChemicalPotentialsGCE(bool resetInitialValues)
void ChangeTPS(ThermalParticleSystem *TPS)
Change the particle list.
virtual void CalculateTwoParticleCorrelations()
Computes the fluctuations and correlations of the primordial particle numbers.
double m_NLOEnergyDensity
ε = ε^GCE(μ*) - (1/2) Σ_j E_j G_jj (LO: ε^GCE(μ*))
int m_CCE
Flag indicating if charm is conserved canonically.
virtual void CalculateFluctuations()
Computes the fluctuation observables.
bool m_PartialZCalculatedFlucts
double m_NLOPressure
P = p^GCE(μ*) - d·T/(2·Vc) (LO: p^GCE(μ*))
double m_MultExp
Exponential multiplier for canonical partition function calculations.
virtual double ParticleScaledVariance(int part)
void SetModelGCE(ThermalModelBase *model)
Sets an external GCE model to use in the saddle-point approach.
std::vector< QuantumNumbers > m_QNvec
A set of QuantumNumbers combinations for which it is necessary to compute the canonical partition fun...
std::vector< double > m_Corr
A vector of chemical factors.
virtual double GetGCEDensity(int i) const
Density of particle species i in the grand-canonical ensemble.
virtual bool IsParticleCanonical(const ThermalParticle &part)
Determines whether the specified ThermalParticle is treat canonically or grand-canonically in the pre...
virtual double CalculateEntropyDensity()
ThermalModelBase * m_modelgce
virtual void CalculatePartitionFunctions(double Vc=-1.)
Calculates all necessary canonical partition functions.
double m_NLOEntropyDensity
LO entropy: s^GCE(μ*) (used only by SaddlePointLO; NLO uses lnZ_SP)
virtual ~ThermalModelCanonical(void)
Destroy the ThermalModelCanonical object.
virtual void CalculateQuantumNumbersRange(bool computeFluctuations=false)
Calculates the range of quantum numbers values for which it is necessary to compute the canonical par...
virtual void FixParametersNoReset()
Method which actually implements ConstrainChemicalPotentialsNoReset() (for backward compatibility).
std::vector< int > m_SaddlePointChargeIndices
Which of {B=0,Q=1,S=2,C=3} are canonical.
virtual double CalculatePressure()
int m_QMAX
Maximum electric charge.
int m_SMAX
Maximum strangeness.
int m_BCE
Flag indicating if baryon charge is conserved canonically.
double m_SaddlePointLogDetSigma
log(det(Sigma))
virtual void ValidateCalculation()
Checks whether issues have occured during the calculation of particle densities in the CalculateDensi...
virtual bool IsConservedChargeCanonical(ConservedCharge::Name charge) const
bool m_PartialZCalculated
bool m_Banalyt
Flag indicating whether the analytical calculation of baryon fugacity is used.
ThermalModelBase * m_modelgce_default
Default ideal-gas GCE model (owned, created once)
int m_SCE
Flag indicating if strangeness is conserved canonically.
std::vector< double > m_SaddlePointMu
Saddle-point mu*_a/T for each conserved charge (size 4)
CanonicalMethod m_Method
Method for computing the canonical partition functions.
virtual double CalculateEnergyDensity()
virtual void FixParameters()
Method which actually implements ConstrainChemicalPotentials() (for backward compatibility).
virtual void CalculatePrimordialDensities()
Calculates the primordial densities of all species.
ThermalModelCanonical(ThermalParticleSystem *TPS, const ThermalModelParameters ¶ms=ThermalModelParameters())
Construct a new ThermalModelCanonical object.
std::vector< double > m_SaddlePointMuStar
Per-particle full chemical potential at saddle point.
Class implementing the Ideal HRG model.
Class containing all information about a particle specie.
int BaryonCharge() const
Particle's baryon number.
int Statistics() const
Particle's statistics.
int Strangeness() const
Particle's strangeness.
void SetCalculationType(IdealGasFunctions::QStatsCalculationType type)
Sets the CalculationType() method to evaluate quantum statistics.
double Density(const ThermalModelParameters ¶ms, IdealGasFunctions::Quantity type=IdealGasFunctions::ParticleDensity, bool useWidth=0, double mu=0.) const
Computes a specified ideal gas thermodynamic function.
IdealGasFunctions::QStatsCalculationType CalculationType() const
Method to evaluate quantum statistics.
int ElectricCharge() const
Particle's electric charge.
int Charm() const
Particle's charm.
int ClusterExpansionOrder() const
Number of terms in the cluster expansion method.
double DensityCluster(int n, const ThermalModelParameters ¶ms, IdealGasFunctions::Quantity type=IdealGasFunctions::ParticleDensity, bool useWidth=0, double mu=0.) const
double ScaledVariance(const ThermalModelParameters ¶ms, bool useWidth=0, double mu=0.) const
Computes the scaled variance of particle number fluctuations in the ideal gas. Computes the scaled va...
Class containing the particle list.
void GetCoefsIntegrateLegendre10(double a, double b, std::vector< double > *x, std::vector< double > *w)
constexpr double Pi()
Pi constant.
double BesselIexp(int n, double x)
Modified Bessel function I_n(x), divided by exponential factor.
The main namespace where all classes and functions of the Thermal-FIST library reside.
@ SaddlePointLO
Saddle-point LO only (GCE densities/thermo, canonical LO fluctuations)
@ SaddlePoint
Saddle-point approximation (per-QN solve)
@ SaddlePointNLO
Saddle-point with systematic NLO expansion (preserves exact conservation laws)
@ GaussLegendre
Composite 10-point Gauss-Legendre on subintervals (default)
Name
Set of all conserved charges considered.
@ BaryonCharge
Baryon number.
@ StrangenessCharge
Strangeness.
@ ElectricCharge
Electric charge.
Struct containing a set of quantum numbers: Baryon number, electric charge, strangeness,...
Structure containing all thermal parameters of the model.
Contains some extra mathematical functions used in the code.