Thermal-FIST 1.6.1
Package for hadron resonance gas model applications
Loading...
Searching...
No Matches
ThermalModelBase.cpp
Go to the documentation of this file.
1/*
2 * Thermal-FIST package
3 *
4 * Copyright (c) 2014-2025 Volodymyr Vovchenko
5 *
6 * GNU General Public License (GPLv3 or later)
7 */
9
10#include <cstdio>
11#include <algorithm>
12#include <cmath>
13#include <limits>
14#include <set>
15#include <sstream>
16
17#include <Eigen/Dense>
18
19#include "HRGBase/Utility.h"
21
22using namespace Eigen;
23
24using namespace std;
25
26namespace thermalfist {
27
28 namespace {
29 std::string ConservedChargeTag(const std::vector<int>& conserved)
30 {
31 const char labels[4] = {'B', 'Q', 'S', 'C'};
32 std::string tag;
33 for (int i = 0; i < 4; ++i) {
34 if (conserved[i] == 1)
35 tag.push_back(labels[i]);
36 }
37 if (tag.empty())
38 tag = "none";
39 return tag;
40 }
41
42 void WarnIllDefinedSoundSpeedOnce(const char* func,
43 const std::vector<int>& conserved,
44 double T,
45 const std::string& reason)
46 {
47 static std::set<std::string> warned;
48 std::ostringstream key;
49 key << func << "|" << ConservedChargeTag(conserved) << "|" << reason;
50 if (warned.insert(key.str()).second) {
51 std::cerr << "**WARNING** " << func << ": ill-defined for conserved set "
52 << ConservedChargeTag(conserved)
53 << " at T = " << T << " GeV. " << reason << std::endl;
54 }
55 }
56
57 bool IsMatrixInvertible(const MatrixXd& m, double threshold = 1e-14)
58 {
59 FullPivLU<MatrixXd> lu(m);
60 lu.setThreshold(threshold);
61 return lu.isInvertible();
62 }
63 } // namespace
64
66 m_TPS(TPS_),
67 m_Parameters(params),
68 m_UseWidth(false),
69 m_PCE(false),
70 m_Calculated(false),
71 m_FeeddownCalculated(false),
72 m_TwoParticleCorrelationsCalculated(false),
73 m_FluctuationsCalculated(false),
74 m_TemperatureDerivativesCalculated(false),
75 m_GCECalculated(false),
76 m_NormBratio(false),
77 m_QuantumStats(true),
78 m_MaxDiff(0.),
79 m_useOpenMP(0),
80 m_IGFExtraConfig()
81 {
84 }
85
86 m_QBgoal = 0.4;
87 m_SBgoal = 50.;
88 m_Chem.resize(m_TPS->Particles().size());
89 m_Volume = params.V;
90 m_densities.resize(m_TPS->Particles().size());
91 m_densitiestotal.resize(m_TPS->Particles().size());
92 m_densitiesbyfeeddown = std::vector< std::vector<double> >(ParticleDecayType::NumberOfDecayTypes, m_densitiestotal);
93
94 m_wprim.resize(m_TPS->Particles().size());
95 m_wtot.resize(m_TPS->Particles().size());
96 m_skewprim.resize(m_TPS->Particles().size());
97 m_skewtot.resize(m_TPS->Particles().size());
98 m_kurtprim.resize(m_TPS->Particles().size());
99 m_kurttot.resize(m_TPS->Particles().size());
100
101 m_ConstrainMuB = false;
102 m_ConstrainMuC = m_ConstrainMuQ = m_ConstrainMuS = true;
103
104 m_ConstrainGammaC = false;
105 m_NccbarGoal = 0.;
106
107 m_Susc.resize(4);
108 for (int i = 0; i < 4; ++i) m_Susc[i].resize(4);
109 m_dSuscdT = m_Susc;
110
111 m_NormBratio = false;
112
113 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
114 const ThermalParticle &tpart = m_TPS->Particles()[i];
115 for (size_t j = 0; j < tpart.Decays().size(); ++j) {
116 if (tpart.DecaysOriginal().size() == tpart.Decays().size() && tpart.Decays()[j].mBratio != tpart.DecaysOriginal()[j].mBratio)
117 m_NormBratio = true;
118 }
119 }
120
121 m_PrimCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(TPS()->ComponentsNumber(), 0.));
122 m_TotalCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(TPS()->ComponentsNumber(), 0.));
123 m_PrimChargesCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(4, 0.));
124 m_FinalChargesCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(4, 0.));
125
126 m_Ensemble = GCE;
127 m_InteractionModel = Ideal;
128
129 //SetStatistics(m_QuantumStats);
130 //SetCalculationType(IdealGasFunctions::Quadratures);
131 SetUseWidth(TPS()->ResonanceWidthIntegrationType());
132
133 ResetCalculatedFlags();
134
135 m_ValidityLog = "";
136 }
137
138
139 void ThermalModelBase::FillVirial(const std::vector<double>& /*ri*/)
140 {
141 }
142
144 {
145 if (!useWidth && m_TPS->ResonanceWidthIntegrationType() != ThermalParticle::ZeroWidth) {
146 m_TPS->SetResonanceWidthIntegrationType(ThermalParticle::ZeroWidth);
147 //m_TPS->ProcessDecays();
148 }
149 if (useWidth && m_TPS->ResonanceWidthIntegrationType() == ThermalParticle::ZeroWidth) {
150 m_TPS->SetResonanceWidthIntegrationType(ThermalParticle::BWTwoGamma);
151 }
152 m_UseWidth = useWidth;
153 }
154
156 {
157 m_UseWidth = (type != ThermalParticle::ZeroWidth);
158 m_TPS->SetResonanceWidthIntegrationType(type);
159 }
160
161
162 void ThermalModelBase::SetNormBratio(bool normBratio) {
163 if (normBratio != m_NormBratio) {
164 m_NormBratio = normBratio;
165 if (m_NormBratio) {
166 m_TPS->NormalizeBranchingRatios();
167 }
168 else {
169 m_TPS->RestoreBranchingRatios();
170 }
171 }
172 }
173
174
175 void ThermalModelBase::ResetChemicalPotentials() {
176 m_Parameters.muS = m_Parameters.muB / 5.;
177 m_Parameters.muQ = -m_Parameters.muB / 50.;
178 m_Parameters.muC = 0.;
179 }
180
181
183 m_Parameters = params;
184 m_Volume = m_Parameters.V;
186 ResetCalculatedFlags();
187 }
188
190 {
191 m_Parameters.T = T;
192 ResetCalculatedFlags();
193 }
194
196 {
197 m_Parameters.muB = muB;
199 ResetCalculatedFlags();
200 }
201
203 {
204 m_Parameters.muQ = muQ;
206 ResetCalculatedFlags();
207 }
208
210 {
211 m_Parameters.muS = muS;
213 ResetCalculatedFlags();
214 }
215
217 {
218 m_Parameters.muC = muC;
220 ResetCalculatedFlags();
221 }
222
223 void ThermalModelBase::SetGammaS(double gammaS)
224 {
225 m_Parameters.gammaS = gammaS;
226 ResetCalculatedFlags();
227 }
228
229 void ThermalModelBase::SetGammaC(double gammaC)
230 {
231 m_Parameters.gammaC = gammaC;
232 ResetCalculatedFlags();
233 }
234
236 {
237 m_Parameters.B = B;
238 ResetCalculatedFlags();
239 }
240
242 {
243 m_Parameters.Q = Q;
244 ResetCalculatedFlags();
245 }
246
248 {
249 m_Parameters.S = S;
250 ResetCalculatedFlags();
251 }
252
254 {
255 m_Parameters.C = C;
256 ResetCalculatedFlags();
257 }
258
260 {
261 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
262 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
263 const ThermalParticle &part1 = TPS()->Particles()[i];
264 const ThermalParticle &part2 = TPS()->Particles()[j];
265 if (part1.BaryonCharge() == 0 && part2.BaryonCharge() == 0)
266 SetVirial(i, j, 0.);
267 }
268 }
269 }
270
272 {
273 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
274 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
275 const ThermalParticle &part1 = TPS()->Particles()[i];
276 const ThermalParticle &part2 = TPS()->Particles()[j];
277 if (part1.BaryonCharge() == 0 && part2.BaryonCharge() == 0)
278 SetAttraction(i, j, 0.);
279 }
280 }
281 }
282
284 {
285 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
286 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
287 const ThermalParticle &part1 = TPS()->Particles()[i];
288 const ThermalParticle &part2 = TPS()->Particles()[j];
289 if ((part1.BaryonCharge() == 0 && part2.BaryonCharge() != 0)
290 || (part1.BaryonCharge() != 0 && part2.BaryonCharge() == 0))
291 SetVirial(i, j, 0.);
292 }
293 }
294 }
295
297 {
298 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
299 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
300 const ThermalParticle &part1 = TPS()->Particles()[i];
301 const ThermalParticle &part2 = TPS()->Particles()[j];
302 if ((part1.BaryonCharge() == 0 && part2.BaryonCharge() != 0)
303 || (part1.BaryonCharge() != 0 && part2.BaryonCharge() == 0))
304 SetAttraction(i, j, 0.);
305 }
306 }
307 }
308
310 {
311 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
312 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
313 const ThermalParticle &part1 = TPS()->Particles()[i];
314 const ThermalParticle &part2 = TPS()->Particles()[j];
315 if ((part1.BaryonCharge() > 0 && part2.BaryonCharge() > 0)
316 || (part1.BaryonCharge() < 0 && part2.BaryonCharge() < 0))
317 SetVirial(i, j, 0.);
318 }
319 }
320 }
321
323 {
324 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
325 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
326 const ThermalParticle &part1 = TPS()->Particles()[i];
327 const ThermalParticle &part2 = TPS()->Particles()[j];
328 if ((part1.BaryonCharge() > 0 && part2.BaryonCharge() > 0)
329 || (part1.BaryonCharge() < 0 && part2.BaryonCharge() < 0))
330 SetAttraction(i, j, 0.);
331 }
332 }
333 }
334
336 {
337 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
338 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
339 const ThermalParticle &part1 = TPS()->Particles()[i];
340 const ThermalParticle &part2 = TPS()->Particles()[j];
341 if ((part1.BaryonCharge() > 0 && part2.BaryonCharge() < 0)
342 || (part1.BaryonCharge() < 0 && part2.BaryonCharge() > 0))
343 SetVirial(i, j, 0.);
344 }
345 }
346 }
347
349 {
350 for (int i = 0; i < TPS()->ComponentsNumber(); ++i) {
351 for (int j = 0; j < TPS()->ComponentsNumber(); ++j) {
352 const ThermalParticle &part1 = TPS()->Particles()[i];
353 const ThermalParticle &part2 = TPS()->Particles()[j];
354 if ((part1.BaryonCharge() > 0 && part2.BaryonCharge() < 0)
355 || (part1.BaryonCharge() < 0 && part2.BaryonCharge() > 0))
356 SetAttraction(i, j, 0.);
357 }
358 }
359 }
360
361 void ThermalModelBase::SetGammaq(double gammaq)
362 {
363 m_Parameters.gammaq = gammaq;
364 ResetCalculatedFlags();
365 }
366
367
369 m_TPS = TPS_;
370 m_Chem.resize(m_TPS->Particles().size());
371 m_densities.resize(m_TPS->Particles().size());
372 m_densitiestotal.resize(m_TPS->Particles().size());
373 m_densitiesbyfeeddown = std::vector< std::vector<double> >(ParticleDecayType::NumberOfDecayTypes, m_densitiestotal);
374 m_wprim.resize(m_TPS->Particles().size());
375 m_wtot.resize(m_TPS->Particles().size());
376 m_skewprim.resize(m_TPS->Particles().size());
377 m_skewtot.resize(m_TPS->Particles().size());
378 m_kurtprim.resize(m_TPS->Particles().size());
379 m_kurttot.resize(m_TPS->Particles().size());
380 m_PrimCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(TPS()->ComponentsNumber(), 0.));
381 m_TotalCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(TPS()->ComponentsNumber(), 0.));
382 m_PrimChargesCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(4, 0.));
383 m_FinalChargesCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(4, 0.));
384 ResetCalculatedFlags();
385 }
386
388 m_QuantumStats = stats;
389 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
390 m_TPS->Particle(i).UseStatistics(stats);
391 }
392
394 {
395 if (!m_UseWidth) {
396 std::cerr << "**WARNING** ThermalModelBase::SetResonanceWidthIntegrationType: Using resonance widths is switched off!" << std::endl;
397 m_TPS->SetResonanceWidthIntegrationType(ThermalParticle::BWTwoGamma);
398 }
399 else
400 m_TPS->SetResonanceWidthIntegrationType(type);
401 }
402
404 m_Chem.resize(m_TPS->Particles().size());
405 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
406 m_Chem[i] = m_TPS->Particles()[i].BaryonCharge() * m_Parameters.muB + m_TPS->Particles()[i].Strangeness() * m_Parameters.muS + m_TPS->Particles()[i].ElectricCharge() * m_Parameters.muQ + m_TPS->Particles()[i].Charm() * m_Parameters.muC;
407 }
408
409 void ThermalModelBase::SetChemicalPotentials(const std::vector<double>& chem)
410 {
411 if (chem.size() != m_TPS->Particles().size()) {
412 std::cerr << "**WARNING** " << m_TAG << "::SetChemicalPotentials(const std::vector<double> & chem): size of chem does not match number of hadrons in the list" << std::endl;
413 return;
414 }
415 m_Chem = chem;
416 }
417
419 {
420 if (i < 0 || i >= static_cast<int>(m_Chem.size())) {
421 throw std::out_of_range("ThermalModelBase::ChemicalPotential: i is out of bounds!");
422 }
423 return m_Chem[i];
424 }
425
427 {
428 if (i < 0 || i >= static_cast<int>(m_Chem.size())) {
429 throw std::out_of_range("ThermalModelBase::SetChemicalPotential: i is out of bounds!");
430 }
431 m_Chem[i] = chem;
432 }
433
435 {
436 if (i < 0 || i >= static_cast<int>(m_Chem.size())) {
437 throw std::out_of_range("ThermalModelBase::FullIdealChemicalPotential: i is out of bounds!");
438 }
439
440 double ret = ChemicalPotential(i);
441
442 ret += MuShift(i);
443
444 const ThermalParticle& part = m_TPS->Particles()[i];
445
446 if (!(m_Parameters.gammaq == 1.)) ret += log(m_Parameters.gammaq) * part.AbsoluteQuark() * m_Parameters.T;
447 if (!(m_Parameters.gammaS == 1. || part.AbsoluteStrangeness() == 0.)) ret += log(m_Parameters.gammaS) * part.AbsoluteStrangeness() * m_Parameters.T;
448 if (!(m_Parameters.gammaC == 1. || part.AbsoluteCharm() == 0.)) ret += log(m_Parameters.gammaC) * part.AbsoluteCharm() * m_Parameters.T;
449
450 return ret;
451 }
452
454 if (m_UseWidth && m_TPS->ResonanceWidthIntegrationType() == ThermalParticle::eBW) {
455 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
456 m_TPS->Particle(i).CalculateThermalBranchingRatios(m_Parameters, m_UseWidth, m_Chem[i] + MuShift(i));
457 }
458 m_TPS->ProcessDecays();
459 }
460
461 // Primordial
462 m_densitiesbyfeeddown[static_cast<int>(Feeddown::Primordial)] = m_densities;
463
464 // According to stability flags
465 int feed_index = static_cast<int>(Feeddown::StabilityFlag);
466 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
467 m_densitiestotal[i] = m_densities[i];
468 //const std::vector< std::pair<double, int> >& decayContributions = m_TPS->Particles()[i].DecayContributionsByFeeddown()[feed_index];
469 const ThermalParticleSystem::DecayContributionsToParticle& decayContributions = m_TPS->DecayContributionsByFeeddown()[feed_index][i];
470 for (size_t j = 0; j < decayContributions.size(); ++j)
471 if (i != decayContributions[j].second)
472 m_densitiestotal[i] += decayContributions[j].first * m_densities[decayContributions[j].second];
473 }
474
475 m_densitiesbyfeeddown[feed_index] = m_densitiestotal;
476
477 // Weak, EM, strong
478 for (feed_index = static_cast<int>(Feeddown::Weak); feed_index <= static_cast<int>(Feeddown::Strong); ++feed_index) {
479 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
480 m_densitiesbyfeeddown[feed_index][i] = m_densities[i];
481 //const std::vector< std::pair<double, int> >& decayContributions = m_TPS->Particles()[i].DecayContributionsByFeeddown()[feed_index];
482 const ThermalParticleSystem::DecayContributionsToParticle& decayContributions = m_TPS->DecayContributionsByFeeddown()[feed_index][i];
483 for (size_t j = 0; j < decayContributions.size(); ++j)
484 if (i != decayContributions[j].second)
485 m_densitiesbyfeeddown[feed_index][i] += decayContributions[j].first * m_densities[decayContributions[j].second];
486 }
487 }
488
489 m_FeeddownCalculated = true;
490 }
491
492
494 {
495 if (resetInitialValues)
497 else
499 }
500
502 // Initial guess for gammaC if constrained
503 if (m_ConstrainGammaC) {
504 m_Parameters.gammaC = 1.0;
505 }
506
507 if (fabs(m_Parameters.muB) < 1e-6 && !m_ConstrainMuB) {
508 if (m_ConstrainMuS)
509 m_Parameters.muS = 0.;
510 if (m_ConstrainMuQ)
511 m_Parameters.muQ = 0.;
512 if (m_ConstrainMuC)
513 m_Parameters.muC = 0.;
516 ConstrainGammaCFromNccbar();
517 return;
518 }
519 if (m_ConstrainMuB) {
520 m_Parameters.muB = xMath::mnucleon() / 2.;
521 }
522 double suppr = 10;
523 if (m_Parameters.muB > 0.150) suppr = 8.;
524 if (m_Parameters.muB > 0.300) suppr = 7.;
525 if (m_Parameters.muB > 0.450) suppr = 6.;
526 if (m_Parameters.muB > 0.600) suppr = 6.;
527 if (m_Parameters.muB > 0.750) suppr = 5.;
528 if (m_Parameters.muB > 0.900) suppr = 4.;
529 if (m_Parameters.muB > 1.000) suppr = 3.;
530 if (m_ConstrainMuS)
531 m_Parameters.muS = m_Parameters.muB / suppr;
532 if (m_ConstrainMuQ)
533 m_Parameters.muQ = -m_Parameters.muB / suppr / 10.;
534 if (m_ConstrainMuC)
535 m_Parameters.muC = -m_Parameters.muS;
536
538 }
539
541 if (fabs(m_Parameters.muB) < 1e-6 && !m_ConstrainMuB) {
542 if (m_ConstrainMuS)
543 m_Parameters.muS = 0.;
544 if (m_ConstrainMuQ)
545 m_Parameters.muQ = 0.;
546 if (m_ConstrainMuC)
547 m_Parameters.muC = 0.;
548 //m_Parameters.muS = m_Parameters.muQ = m_Parameters.muC = 0.;
551 ConstrainGammaCFromNccbar();
552 return;
553 }
554
555 m_ConstrainMuB &= m_TPS->hasBaryons();
556 m_ConstrainMuQ &= (m_TPS->hasCharged() && m_TPS->hasBaryons());
557 m_ConstrainMuS &= m_TPS->hasStrange();
558 m_ConstrainMuC &= m_TPS->hasCharmed();
559
560 vector<double> x22(4);
561 x22[0] = m_Parameters.muB;
562 x22[1] = m_Parameters.muQ;
563 x22[2] = m_Parameters.muS;
564 x22[3] = m_Parameters.muC;
565 vector<double> x2(4), xinit(4);
566 xinit[0] = x2[0] = m_Parameters.muB;
567 xinit[1] = x2[1] = m_Parameters.muQ;
568 xinit[2] = x2[2] = m_Parameters.muS;
569 xinit[3] = x2[3] = m_Parameters.muC;
570 int iter = 0, iterMAX = 2;
571 while (iter < iterMAX) {
572 BroydenEquationsChem eqs(this);
573 BroydenJacobianChem jaco(this);
574 BroydenChem broydn(this, &eqs, &jaco);
576 broydn.Solve(x22, &crit);
577 //std::cerr << "Broyden iters: " << broydn.Iterations() << std::endl;
578 break;
579 }
580
581 ConstrainGammaCFromNccbar();
582 }
583
584 void ThermalModelBase::ConstrainGammaCFromNccbar() {
585 if (!m_ConstrainGammaC || m_NccbarGoal <= 0.)
586 return;
587
588 if (!m_TPS->hasCharmed()) {
589 std::cerr << "**WARNING** ConstrainGammaCFromNccbar: "
590 << "No charmed particles in the list, cannot constrain gammaC" << std::endl;
591 return;
592 }
593
594 double V = m_Parameters.V;
595 double targetNccbar = m_NccbarGoal;
596
597 // Helper: given log(gammaC), set gammaC, constrain mu's, return Nccbar residual
598 auto computeResidual = [&](double log_gammaC) -> double {
599 double gc = exp(log_gammaC);
600 SetGammaC(gc);
601
602 // Refit constrained chemical potentials for this gammaC (without resetting initial guesses)
603 bool anyMuConstrained = m_ConstrainMuB || m_ConstrainMuQ || m_ConstrainMuS || m_ConstrainMuC;
604 if (anyMuConstrained) {
605 // Temporarily disable gammaC constraint to avoid recursion
606 m_ConstrainGammaC = false;
608 m_ConstrainGammaC = true;
609 }
610 else {
613 }
614
615 double absCharmDens = CalculateAbsoluteCharmDensity();
616 double currentNccbar = V * absCharmDens / 2.0;
617 // Guard against NaN from overflow in density calculation (e.g. Bessel overflow)
618 if (!std::isfinite(currentNccbar))
619 return std::numeric_limits<double>::max();
620 return currentNccbar - targetNccbar;
621 };
622
623 // Initial points for secant method
624 double x0 = log(std::max(m_Parameters.gammaC, 1.e-4));
625 double x1 = x0 + 0.1;
626
627 double f0 = computeResidual(x0);
628 double f1 = computeResidual(x1);
629
630 const int maxIter = 50;
631 const double tol = 1.e-8;
632
633 int iter = 0;
634 for (iter = 0; iter < maxIter; ++iter) {
635 if (fabs(f1) < tol * std::max(1.0, targetNccbar))
636 break;
637
638 if (fabs(f1 - f0) < 1.e-15) {
639 // Secant slope too small, perturb
640 x1 += 0.05;
641 f1 = computeResidual(x1);
642 continue;
643 }
644
645 double x2 = x1 - f1 * (x1 - x0) / (f1 - f0);
646
647 // Clamp to reasonable range: gammaC in [1e-4, 1000]
648 x2 = std::max(x2, log(1.e-4));
649 x2 = std::min(x2, log(1000.0));
650
651 x0 = x1;
652 f0 = f1;
653 x1 = x2;
654 f1 = computeResidual(x1);
655 }
656
657 if (iter == maxIter) {
658 std::cerr << "**WARNING** ConstrainGammaCFromNccbar: "
659 << "Secant method did not converge after " << maxIter
660 << " iterations, |f| = " << fabs(f1) << std::endl;
661 }
662
663 // gammaC is already set by the last computeResidual call
664 }
665
666 bool ThermalModelBase::FixChemicalPotentialsThroughDensities(double rhoB, double rhoQ, double rhoS, double rhoC,
667 double muBinit, double muQinit, double muSinit, double muCinit,
668 bool ConstrMuB, bool ConstrMuQ, bool ConstrMuS, bool ConstrMuC) {
669 double V = Parameters().V;
671 V * rhoB, V * rhoQ, V * rhoS, V * rhoC,
672 muBinit, muQinit, muSinit, muCinit,
673 ConstrMuB, ConstrMuQ, ConstrMuS, ConstrMuC);
674 }
675
676 bool ThermalModelBase::SolveChemicalPotentials(double totB, double totQ, double totS, double totC,
677 double muBinit, double muQinit, double muSinit, double muCinit,
678 bool ConstrMuB, bool ConstrMuQ, bool ConstrMuS, bool ConstrMuC) {
680 std::cerr << "**WARNING** PCE enabled, cannot assume chemical equilibrium to do optimization..." << std::endl;
681 return false;
682 }
683
684 // TODO: Stability analysis for small densities/numbers
685 // if (abs(totB) < 1.e-6)
686 // totB = 0.;
687 // if (abs(totQ) < 1.e-6)
688 // totQ = 0.;
689 // if (abs(totS) < 1.e-6)
690 // totS = 0.;
691 // if (abs(totC) < 1.e-6)
692 // totC = 0.;
693
694 if (ConstrMuB)
695 m_Parameters.muB = muBinit;
696 if (ConstrMuS)
697 m_Parameters.muS = muSinit;
698 if (ConstrMuQ)
699 m_Parameters.muQ = muQinit;
700 if (ConstrMuC)
701 m_Parameters.muC = muCinit;
702 bool allzero = true;
703 allzero &= (totB == 0.0 && ConstrMuB) || (muBinit == 0 && !ConstrMuB);
704 allzero &= (totQ == 0.0 && ConstrMuQ) || (muQinit == 0 && !ConstrMuQ);
705 allzero &= (totS == 0.0 && ConstrMuS) || (muSinit == 0 && !ConstrMuS);
706 allzero &= (totC == 0.0 && ConstrMuC) || (muCinit == 0 && !ConstrMuC);
707
708
709 if (allzero) {
710 m_Parameters.muB = 0.;
711 m_Parameters.muS = 0.;
712 m_Parameters.muQ = 0.;
713 m_Parameters.muC = 0.;
716 return true;
717 }
718 vector<int> vConstr(4, 1);
719 vector<int> vType(4, 0);
720
721
722 vConstr[0] = m_TPS->hasBaryons() && ConstrMuB;
723 vConstr[1] = m_TPS->hasCharged() && ConstrMuQ;
724 vConstr[2] = m_TPS->hasStrange() && ConstrMuS;
725 vConstr[3] = m_TPS->hasCharmed() && ConstrMuC;
726
727 vType[0] = (int)(totB == 0.0);
728 vType[1] = (int)(totQ == 0.0);
729 vType[2] = (int)(totS == 0.0);
730 vType[3] = (int)(totC == 0.0);
731
732 vector<double> vTotals(4);
733 vTotals[0] = totB;
734 vTotals[1] = totQ;
735 vTotals[2] = totS;
736 vTotals[3] = totC;
737
738 vector<double> xin(4, 0.);
739 xin[0] = muBinit;
740 xin[1] = muQinit;
741 xin[2] = muSinit;
742 xin[3] = muCinit;
743
744 vector<double> xinactual;
745 for (int i = 0; i < 4; ++i) {
746 if (vConstr[i]) {
747 xinactual.push_back(xin[i]);
748 }
749 }
750
751 BroydenEquationsChemTotals eqs(vConstr, vType, vTotals, this);
752 BroydenJacobianChemTotals jaco(vConstr, vType, vTotals, this);
753 Broyden broydn(&eqs, &jaco);
755 broydn.Solve(xinactual, &crit);
756
757
758 //std::cerr << BaryonDensity() * Volume() << std::endl;
759
760 return (broydn.Iterations() < broydn.MaxIterations());
761 }
762
769
771 {
772 m_ValidityLog = "";
773
774 char cc[1000];
775
776 m_LastCalculationSuccessFlag = true;
777 for (size_t i = 0; i < m_densities.size(); ++i) {
778 if (m_densities[i] != m_densities[i]) {
779 m_LastCalculationSuccessFlag = false;
780
781 sprintf(cc, "Density for particle %lld (%s) is NaN!\n", m_TPS->Particle(i).PdgId(), m_TPS->Particle(i).Name().c_str());
782 std::cerr << "**WARNING** " << cc << std::endl;
783
784
785 m_ValidityLog.append(cc);
786 }
787 //m_LastCalculationSuccessFlag &= (m_densities[i] == m_densities[i]);
788 }
789 }
790
791 std::vector<double> ThermalModelBase::CalculateChargeFluctuations(const std::vector<double>& /*chgs*/, int /*order*/, bool /*dimensionfull*/)
792 {
793 std::cerr << "**WARNING** " << m_TAG << "::CalculateChargeFluctuations(const std::vector<double>& chgs, int order) not implemented!" << std::endl;
794 return std::vector<double>();
795 }
796
797 std::vector<double> ThermalModelBase::CalculateGeneralizedSusceptibilities(const std::vector<std::vector<double>> &/*chgs*/)
798 {
799 throw std::runtime_error("ThermalModelBase::CalculateGeneralizedSusceptibilities: not implemented!");
800 }
801
802 double ThermalModelBase::CalculateHadronDensity() {
803 if (!m_Calculated) CalculateDensities();
804 double ret = 0.;
805 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
806 ret += m_densities[i];
807
808 return ret;
809 }
810
811 double ThermalModelBase::CalculateBaryonDensity() {
812 if (!m_Calculated) CalculateDensities();
813 double ret = 0.;
814 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
815 ret += m_TPS->Particles()[i].BaryonCharge() * m_densities[i];
816
817 return ret;
818 }
819
820 double ThermalModelBase::CalculateChargeDensity() {
821 if (!m_Calculated) CalculateDensities();
822 double ret = 0.;
823 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
824 ret += m_TPS->Particles()[i].ElectricCharge() * m_densities[i];
825
826 return ret;
827 }
828
829 double ThermalModelBase::CalculateStrangenessDensity() {
830 if (!m_Calculated) CalculateDensities();
831 double ret = 0.;
832 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
833 ret += m_TPS->Particles()[i].Strangeness() * m_densities[i];
834
835 return ret;
836 }
837
838 double ThermalModelBase::CalculateCharmDensity() {
839 if (!m_Calculated) CalculateDensities();
840 double ret = 0.;
841 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
842 ret += m_TPS->Particles()[i].Charm() * m_densities[i];
843 return ret;
844 }
845
846 double ThermalModelBase::CalculateAbsoluteBaryonDensity() {
847 if (!m_Calculated) CalculateDensities();
848 double ret = 0.;
849 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
850 ret += fabs((double)m_TPS->Particles()[i].BaryonCharge()) * m_densities[i];
851 return ret;
852 }
853
854 double ThermalModelBase::CalculateAbsoluteChargeDensity() {
855 if (!m_Calculated) CalculateDensities();
856 double ret = 0.;
857 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
858 ret += fabs((double)m_TPS->Particles()[i].ElectricCharge()) * m_densities[i];
859 return ret;
860 }
861
862 double ThermalModelBase::CalculateAbsoluteStrangenessDensity() {
863 if (!m_Calculated) CalculateDensities();
864 double ret = 0.;
865 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
866 ret += m_TPS->Particles()[i].AbsoluteStrangeness() * m_densities[i];
867 return ret;
868 }
869
870 double ThermalModelBase::CalculateAbsoluteStrangenessDensityModulo() {
871 if (!m_Calculated) CalculateDensities();
872 double ret = 0.;
873 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
874 ret += fabs((double)m_TPS->Particles()[i].Strangeness()) * m_densities[i];
875 return ret;
876 }
877
878 double ThermalModelBase::CalculateAbsoluteCharmDensity() {
879 if (!m_Calculated) CalculateDensities();
880 double ret = 0.;
881 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
882 ret += m_TPS->Particles()[i].AbsoluteCharm() * m_densities[i];
883 return ret;
884 }
885
886 double ThermalModelBase::CalculateAbsoluteCharmDensityModulo() {
887 if (!m_Calculated) CalculateDensities();
888 double ret = 0.;
889 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
890 ret += fabs((double)m_TPS->Particles()[i].Charm()) * m_densities[i];
891 return ret;
892 }
893
894 double ThermalModelBase::CalculateArbitraryChargeDensity() {
895 if (!m_Calculated) CalculateDensities();
896 double ret = 0.;
897 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i)
898 ret += m_TPS->Particles()[i].ArbitraryCharge() * m_densities[i];
899 return ret;
900 }
901
902
903 double ThermalModelBase::GetDensity(long long PDGID, const std::vector<double> *dens)
904 {
905 if (m_TPS->PdgToId(PDGID) != -1)
906 return dens->operator[](m_TPS->PdgToId(PDGID));
907
908 // 1 - Npart
909 if (PDGID == 1) return CalculateBaryonDensity();
910
911 // K0S or K0L
912 if (PDGID == 310 || PDGID == 130)
913 if (m_TPS->PdgToId(311) != -1 && m_TPS->PdgToId(-311) != -1)
914 return (dens->operator[](m_TPS->PdgToId(311)) + dens->operator[](m_TPS->PdgToId(-311))) / 2.;
915
916 // Id Pdg code has a trailing zero, try to construct a particle + anti-particle yield
917 if (PDGID % 10 == 0) {
918 long long tpdgid = PDGID / 10;
919 if (m_TPS->PdgToId(tpdgid) != -1 && m_TPS->PdgToId(-tpdgid) != -1)
920 return dens->operator[](m_TPS->PdgToId(tpdgid)) + dens->operator[](m_TPS->PdgToId(-tpdgid));
921 }
922
923 // 22122112 - nucleons
924 if (PDGID == 22122112 && m_TPS->PdgToId(2212) != -1 && m_TPS->PdgToId(2112) != -1)
925 return dens->operator[](m_TPS->PdgToId(2212)) + dens->operator[](m_TPS->PdgToId(2112));
926
927 std::cerr << "**WARNING** " << m_TAG << ": Density with PDG ID " << PDGID << " not found!" << std::endl;
928
929 return 0.;
930 }
931
932 double ThermalModelBase::GetDensity(long long PDGID, Feeddown::Type feeddown)
933 {
934 std::vector<double> *dens = NULL;
935 if (feeddown == Feeddown::Primordial)
936 dens = &m_densities;
937 else if (feeddown == Feeddown::StabilityFlag)
938 dens = &m_densitiestotal;
939 else if (static_cast<size_t>(feeddown) < m_densitiesbyfeeddown.size())
940 dens = &m_densitiesbyfeeddown[static_cast<int>(feeddown)];
941 else {
942 std::cerr << "**WARNING** " << m_TAG << ": GetDensity: Unknown feeddown: " << static_cast<int>(feeddown) << std::endl;
943 return 0.;
944 }
945
946 if (!m_Calculated)
948
949 if (feeddown != Feeddown::Primordial && !m_FeeddownCalculated)
951
952 double ret = GetDensity(PDGID, dens);
953
954 // Weak decay contributions from K0S if this particle is not in the list
955 if (feeddown == Feeddown::Weak && m_TPS->PdgToId(310) == -1) {
956 // pi0
957 if (PDGID == 111) {
958 ret += 2. * 0.308 * GetDensity(310, dens);
959 }
960 // pi+,-
961 if (PDGID == 211 || PDGID == -211) {
962 ret += 0.692 * GetDensity(310, dens);
963 }
964 }
965
966 return ret;
967 }
968
969
970 std::vector<double> ThermalModelBase::GetIdealGasDensities() const {
971 std::vector<double> ret = m_densities;
972 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
973 ret[i] = m_TPS->Particles()[i].Density(m_Parameters, IdealGasFunctions::ParticleDensity, m_UseWidth, m_Chem[i]);
974 }
975 return ret;
976 }
977
978 void ThermalModelBase::ResetCalculatedFlags()
979 {
980 m_Calculated = false;
981 m_FeeddownCalculated = false;
982 m_TwoParticleCorrelationsCalculated = false;
983 m_SusceptibilitiesCalculated = false;
984 m_FluctuationsCalculated = false;
985 m_TemperatureDerivativesCalculated = false;
986 m_GCECalculated = false;
987 }
988
989 double ThermalModelBase::ConservedChargeDensity(ConservedCharge::Name chg)
990 {
992 return BaryonDensity();
994 return ElectricChargeDensity();
996 return StrangenessDensity();
998 return CharmDensity();
999 return 0.0;
1000 }
1001
1002 double ThermalModelBase::AbsoluteConservedChargeDensity(ConservedCharge::Name chg)
1003 {
1005 return AbsoluteBaryonDensity();
1007 return AbsoluteElectricChargeDensity();
1009 return AbsoluteStrangenessDensity();
1011 return AbsoluteCharmDensity();
1012 return 0.0;
1013 }
1014
1015 double ThermalModelBase::ConservedChargeDensitydT(ConservedCharge::Name chg)
1016 {
1017 if (!IsTemperatureDerivativesCalculated())
1019
1020 double ret = 0.0;
1021 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
1022 ret += m_TPS->Particles()[i].ConservedCharge(chg) * m_dndT[i];
1023 }
1024
1025 return ret;
1026 }
1027
1028 double ThermalModelBase::ChargedMultiplicity(int type)
1029 {
1030 if (!m_Calculated) CalculateDensities();
1031 double ret = 0.0;
1032 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
1033 int tQ = m_TPS->Particles()[i].ElectricCharge();
1034 bool fl = false;
1035 if (type == 0 && tQ != 0)
1036 fl = true;
1037 if (type == 1 && tQ > 0)
1038 fl = true;
1039 if (type == -1 && tQ < 0)
1040 fl = true;
1041 if (fl)
1042 ret += m_densities[i];
1043 }
1044 return ret * Volume();
1045 }
1046
1047 double ThermalModelBase::ChargedScaledVariance(int type)
1048 {
1049 if (!m_FluctuationsCalculated) {
1050 std::cerr << "**WARNING** " << m_TAG << ": ChargedScaledVariance(int): Fluctuations were not calculated\n";
1051 return 1.;
1052 }
1053 double ret = 0.0;
1054 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
1055 int tQ = m_TPS->Particles()[i].ElectricCharge();
1056 bool fl = false;
1057 if (type == 0 && tQ != 0)
1058 fl = true;
1059 if (type == 1 && tQ > 0)
1060 fl = true;
1061 if (type == -1 && tQ < 0)
1062 fl = true;
1063 if (fl) {
1064 for (int j = 0; j < m_TPS->ComponentsNumber(); ++j) {
1065 int tQ2 = m_TPS->Particles()[j].ElectricCharge();
1066 bool fl2 = false;
1067 if (type == 0 && tQ2 != 0)
1068 fl2 = true;
1069 if (type == 1 && tQ2 > 0)
1070 fl2 = true;
1071 if (type == -1 && tQ2 < 0)
1072 fl2 = true;
1073
1074 if (fl2) {
1075 ret += m_PrimCorrel[i][j];
1076 }
1077 }
1078 }
1079 }
1080 return ret * m_Parameters.T * Volume() / ChargedMultiplicity(type);
1081 }
1082
1083 double ThermalModelBase::ChargedMultiplicityFinal(int type)
1084 {
1085 if (!m_Calculated) CalculateDensities();
1086
1087 int op = type;
1088 if (type == -1)
1089 op = 2;
1090
1091 double ret = 0.0;
1092 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
1093 ret += m_densities[i] * m_TPS->Particles()[i].Nch()[op];
1094 }
1095 return ret * Volume();
1096 }
1097
1098 double ThermalModelBase::ChargedScaledVarianceFinal(int type)
1099 {
1100 if (!m_FluctuationsCalculated) {
1101 std::cerr << "**WARNING** " << m_TAG << ": ChargedScaledVarianceFinal(int): Fluctuations were not calculated" << std::endl;
1102 return 1.;
1103 }
1104 int op = type;
1105 if (type == -1)
1106 op = 2;
1107 double ret = 0.0;
1108 for (int i = 0; i < m_TPS->ComponentsNumber(); ++i) {
1109 ret += m_densities[i] * Volume() * m_TPS->Particles()[i].DeltaNch()[op];
1110 for (int j = 0; j < m_TPS->ComponentsNumber(); ++j) {
1111 ret += m_PrimCorrel[i][j] * m_Parameters.T * Volume() * m_TPS->Particles()[i].Nch()[op] * m_TPS->Particles()[j].Nch()[op];
1112 }
1113 }
1114 return ret / ChargedMultiplicityFinal(type);
1115 }
1116
1118 throw std::runtime_error("ThermalModelBase::CalculateTwoParticleCorrelations: Calculation of two-particle correlations and fluctuations is not implemented");
1119 }
1120
1121
1123 {
1124 // Decay contributions here are done according to Eq. (47) in nucl-th/0606036
1125
1126 int NN = m_densities.size();
1127
1128 // Fluctuations for all
1129 for (int i = 0; i < NN; ++i)
1130 //for(int j=0;j<NN;++j)
1131 {
1132 m_TotalCorrel[i][i] = m_PrimCorrel[i][i];
1133 //for (int r = 0; r < m_TPS->Particles()[i].DecayContributions().size(); ++r) {
1134 const ThermalParticleSystem::DecayContributionsToParticle& decayContributions = m_TPS->DecayContributionsByFeeddown()[Feeddown::StabilityFlag][i];
1135 for (size_t r = 0; r < decayContributions.size(); ++r) {
1136 int rr = decayContributions[r].second;
1137
1138 m_TotalCorrel[i][i] += 2. * m_PrimCorrel[i][rr] * decayContributions[r].first;
1139
1140 for (size_t r2 = 0; r2 < decayContributions.size(); ++r2) {
1141 int rr2 = decayContributions[r2].second;
1142 m_TotalCorrel[i][i] += m_PrimCorrel[rr][rr2] * decayContributions[r].first * decayContributions[r2].first;
1143 }
1144
1145 // Probabilistic decays
1146 m_TotalCorrel[i][i] += m_densities[rr] / m_Parameters.T * m_TPS->DecayCumulants()[i][r].first[1];
1147 //m_TotalCorrel[i][i] += m_densities[rr] / m_Parameters.T * m_TPS->Particles()[i].DecayCumulants()[r].first[1];
1148 //if (rr != i) { // && !m_TPS->Particles()[r].IsStable()) {
1149 // double nij = 0., ni = 0., nj = 0., dnij = 0.;
1150 // const auto& decayDistributions = TPS()->ResonanceFinalStatesDistributions()[rr];
1151 // for (size_t br = 0; br < decayDistributions.size(); ++br) {
1152 // nij += decayDistributions[br].first * decayDistributions[br].second[i] * decayDistributions[br].second[i];
1153 // ni += decayDistributions[br].first * decayDistributions[br].second[i];
1154 // nj += decayDistributions[br].first * decayDistributions[br].second[i];
1155 // }
1156 // dnij = nij - ni * nj;
1157 // m_TotalCorrel[i][i] += m_densities[rr] / Parameters().T * dnij;
1158 //}
1159 }
1160 }
1161
1162
1163 // Correlations only for stable
1164 for (int i = 0; i < NN; ++i) {
1165 if (m_TPS->Particles()[i].IsStable()) {
1166 for (int j = 0; j < NN; ++j) {
1167 if (j != i && m_TPS->Particles()[j].IsStable()) {
1168 m_TotalCorrel[i][j] = m_PrimCorrel[i][j];
1169
1170 const ThermalParticleSystem::DecayContributionsToParticle& decayContributionsI = m_TPS->DecayContributionsByFeeddown()[Feeddown::StabilityFlag][i];
1171 const ThermalParticleSystem::DecayContributionsToParticle& decayContributionsJ = m_TPS->DecayContributionsByFeeddown()[Feeddown::StabilityFlag][j];
1172
1173 for (size_t r = 0; r < decayContributionsJ.size(); ++r) {
1174 int rr = decayContributionsJ[r].second;
1175 m_TotalCorrel[i][j] += m_PrimCorrel[i][rr] * decayContributionsJ[r].first;
1176 }
1177
1178 for (size_t r = 0; r < decayContributionsI.size(); ++r) {
1179 int rr = decayContributionsI[r].second;
1180 m_TotalCorrel[i][j] += m_PrimCorrel[j][rr] * decayContributionsI[r].first;
1181 }
1182
1183 for (size_t r = 0; r < decayContributionsI.size(); ++r) {
1184 int rr = decayContributionsI[r].second;
1185
1186 for (size_t r2 = 0; r2 < decayContributionsJ.size(); ++r2) {
1187 int rr2 = decayContributionsJ[r2].second;
1188 m_TotalCorrel[i][j] += m_PrimCorrel[rr][rr2] * decayContributionsI[r].first * decayContributionsJ[r2].first;
1189 }
1190 }
1191
1192 // Contribution from probabilistic decays
1193 for (int r = 0; r < m_TPS->ComponentsNumber(); ++r) {
1194 if (r != i && r != j) { // && !m_TPS->Particles()[r].IsStable()) {
1195 double nij = 0., ni = 0., nj = 0., dnij = 0.;
1196 //const ThermalParticle &tpart = m_TPS->Particle(r);
1197 const ThermalParticleSystem::ResonanceFinalStatesDistribution &decayDistributions = m_TPS->ResonanceFinalStatesDistributions()[r];
1198 for (size_t br = 0; br < decayDistributions.size(); ++br) {
1199 nij += decayDistributions[br].first * decayDistributions[br].second[i] * decayDistributions[br].second[j];
1200 ni += decayDistributions[br].first * decayDistributions[br].second[i];
1201 nj += decayDistributions[br].first * decayDistributions[br].second[j];
1202 }
1203 dnij = nij - ni * nj;
1204 m_TotalCorrel[i][j] += m_densities[r] / m_Parameters.T * dnij;
1205 }
1206 }
1207 }
1208 }
1209 }
1210 }
1211
1212 for (int i = 0; i < NN; ++i) {
1213 m_wtot[i] = m_TotalCorrel[i][i];
1214 if (m_densitiestotal[i] > 0.) m_wtot[i] *= m_Parameters.T / m_densitiestotal[i];
1215 else m_wtot[i] = 1.;
1216 // Guard against NaN propagated from singular correlation matrices
1217 if (m_wtot[i] != m_wtot[i]) m_wtot[i] = 1.;
1218 }
1219 }
1220
1222 {
1223 double nB = 0., nBbar = 0.;
1224 for(int i = 0; i < ComponentsNumber(); ++i) {
1225 if(TPS()->Particles()[i].BaryonCharge() == 1) nB += m_densities[i];
1226 if(TPS()->Particles()[i].BaryonCharge() == -1) nBbar += m_densities[i];
1227 }
1228
1229 double chi1B = nB / pow(m_Parameters.T * xMath::GeVtoifm(), 3);
1230 double chi1Bbar = nBbar / pow(m_Parameters.T * xMath::GeVtoifm(), 3);
1231
1232 double gammaB = gammaBbar * nBbar / nB + (nB - nBbar) / nB;
1233 double gammaBav = (gammaB * nB + gammaBbar * nBbar) / (nB + nBbar);
1234
1235 double wp = 0., wm = 0.;
1236 for(int i = 0; i < ComponentsNumber(); ++i) {
1237 const ThermalParticle &part = TPS()->Particles()[i];
1238 if (part.BaryonCharge() != 1 && part.BaryonCharge() != -1) continue;
1239
1240 for(int j = 0; j < ComponentsNumber(); ++j) {
1241 const ThermalParticle &part2 = TPS()->Particles()[j];
1242 if (part2.BaryonCharge() != 1 && part2.BaryonCharge() != -1) continue;
1243
1244
1245 if (part.BaryonCharge() == 1 && part2.BaryonCharge() == 1)
1246 wp += m_PrimCorrel[i][j];
1247 if (part.BaryonCharge() == -1 && part2.BaryonCharge() == -1)
1248 wm += m_PrimCorrel[i][j];
1249 }
1250 }
1251 wp = wp * m_Parameters.T / nB;
1252 wm = wm * m_Parameters.T / nBbar;
1253
1254 double wp0 = wp, wm0 = wm;
1255 wp = (1. - gammaB + gammaB * wp0);
1256 wm = (1. - gammaBbar + gammaBbar * wm0);
1257
1258 double deltachipm = wp * nB * gammaB - wp0 * nB + wm * nBbar * gammaBbar - wm0 * nBbar;
1259 deltachipm *= 1. / m_Parameters.T;
1260
1261 for(int i = 0; i < ComponentsNumber(); ++i) {
1262 const ThermalParticle &part = TPS()->Particles()[i];
1263 if (part.BaryonCharge() != 1 && part.BaryonCharge() != -1) continue;
1264 for(int j = 0; j < ComponentsNumber(); ++j) {
1265 const ThermalParticle &part2 = TPS()->Particles()[j];
1266 if (part2.BaryonCharge() != 1 && part2.BaryonCharge() != -1) continue;
1267
1268 if (i == j) {
1269 double gamma = (part.BaryonCharge() == 1) ? gammaB : gammaBbar;
1270 double chi1i = m_densities[i];
1271 double chi2i = m_PrimCorrel[i][j] * m_Parameters.T;
1272 double chi2i_new = gamma * (1. - gamma) * chi1i + gamma * gamma * chi2i;
1273 m_PrimCorrel[i][j] = chi2i_new / m_Parameters.T;
1274 continue;
1275 }
1276
1277 if (part.BaryonCharge() == 1 && part2.BaryonCharge() == 1) {
1278 double gamma = gammaB;
1279 m_PrimCorrel[i][j] = gamma * gamma * m_PrimCorrel[i][j];
1280 continue;
1281 }
1282
1283 if (part.BaryonCharge() == -1 && part2.BaryonCharge() == -1) {
1284 double gamma = gammaBbar;
1285 m_PrimCorrel[i][j] = gamma * gamma * m_PrimCorrel[i][j];
1286 continue;
1287 }
1288
1289 m_PrimCorrel[i][j] += deltachipm * m_densities[i] * m_densities[j] / nB / nBbar;
1290 }
1291 }
1292
1293 for(int i = 0; i < ComponentsNumber(); ++i) {
1294 if(TPS()->Particles()[i].BaryonCharge() == 1)
1295 m_densities[i] *= gammaB;
1296 if(TPS()->Particles()[i].BaryonCharge() == -1)
1297 m_densities[i] *= gammaBbar;
1298 }
1299
1303 }
1304
1306 {
1307 if (!IsFluctuationsCalculated()) {
1308 throw std::runtime_error("ThermalModelBase::TwoParticleSusceptibilityPrimordial: fluctuations were not computed beforehand!");
1309 }
1310
1311 return m_PrimCorrel[i][j] / m_Parameters.T / m_Parameters.T / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
1312 }
1313
1315 {
1316 int i = TPS()->PdgToId(id1);
1317 int j = TPS()->PdgToId(id2);
1318
1319 if (i == -1) {
1320 std::cerr << "**WARNING** ThermalModelBase::TwoParticleSusceptibilityPrimordialByPdg: unknown pdg code " << id1 << std::endl;
1321 return 0.;
1322 }
1323 if (j == -1) {
1324 std::cerr << "**WARNING** ThermalModelBase::TwoParticleSusceptibilityPrimordialByPdg: unknown pdg code " << id2 << std::endl;
1325 return 0.;
1326 }
1327
1329 }
1330
1332 if (!IsFluctuationsCalculated() || !IsTemperatureDerivativesCalculated()) {
1333 throw std::runtime_error("ThermalModelBase::TwoParticleSusceptibilityPrimordial: temperature derivatives of fluctuations were not computed beforehand!");
1334 }
1335
1336 return m_PrimChi2sdT[i][j];
1337 }
1338
1340 {
1341 int i = TPS()->PdgToId(id1);
1342 int j = TPS()->PdgToId(id2);
1343
1344 if (i == -1) {
1345 std::cerr << "**WARNING** ThermalModelBase::TwoParticleSusceptibilityTemperatureDerivativePrimordialByPdg: unknown pdg code " << id1 << std::endl;
1346 return 0.;
1347 }
1348 if (j == -1) {
1349 std::cerr << "**WARNING** ThermalModelBase::TwoParticleSusceptibilityTemperatureDerivativePrimordialByPdg: unknown pdg code " << id2 << std::endl;
1350 return 0.;
1351 }
1352
1354 }
1355
1357 {
1358 int i1 = TPS()->PdgToId(id1);
1359 int j1 = TPS()->PdgToId(id2);
1360
1361 if (i1 == -1) {
1362 std::cerr << "**WARNING** ThermalModelBase::NetParticleSusceptibilityPrimordialByPdg: unknown pdg code " << id1 << std::endl;
1363 return 0.;
1364 }
1365 if (j1 == -1) {
1366 std::cerr << "**WARNING** ThermalModelBase::NetParticleSusceptibilityPrimordialByPdg: unknown pdg code " << id2 << std::endl;
1367 return 0.;
1368 }
1369
1370 int i2 = TPS()->PdgToId(-id1);
1371 int j2 = TPS()->PdgToId(-id2);
1372
1373 // Both particles are neutral
1374 if (i2 == -1 && j2 == -1) {
1376 }
1377
1378 // First particle species is neutral
1379 if (i2 == -1) {
1381 }
1382
1383 // Second particle species is neutral
1384 if (j2 == -1) {
1386 }
1387
1388 // Both particles have antiparticles
1390 }
1391
1393 {
1394 if (!IsFluctuationsCalculated()) {
1395 throw std::runtime_error("ThermalModelBase::TwoParticleSusceptibilityFinal: fluctuations were not computed beforehand!");
1396 }
1397
1398 if (!m_TPS->Particle(i).IsStable() || !m_TPS->Particle(j).IsStable()) {
1399 int tid = i;
1400 if (!m_TPS->Particle(j).IsStable())
1401 tid = j;
1402 throw std::runtime_error("ThermalModelBase::TwoParticleSusceptibilityFinal: Particle " + std::to_string(tid) + " is not stable! Final correlations not computed for unstable particles.");
1403 }
1404
1405 return m_TotalCorrel[i][j] / m_Parameters.T / m_Parameters.T / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
1406 }
1407
1409 {
1410 int i = TPS()->PdgToId(id1);
1411 int j = TPS()->PdgToId(id2);
1412
1413 if (i == -1) {
1414 std::cerr << "**WARNING** ThermalModelBase::TwoParticleSusceptibilityFinalByPdg: unknown pdg code " << id1 << std::endl;
1415 return 0.;
1416 }
1417 if (j == -1) {
1418 std::cerr << "**WARNING** ThermalModelBase::TwoParticleSusceptibilityFinalByPdg: unknown pdg code " << id2 << std::endl;
1419 return 0.;
1420 }
1421
1422 return TwoParticleSusceptibilityFinal(i, j);
1423 }
1424
1426 {
1427 int i1 = TPS()->PdgToId(id1);
1428 int j1 = TPS()->PdgToId(id2);
1429
1430 if (i1 == -1) {
1431 std::cerr << "**WARNING** ThermalModelBase::NetParticleSusceptibilityFinalByPdg: unknown pdg code " << id1 << std::endl;
1432 return 0.;
1433 }
1434 if (j1 == -1) {
1435 std::cerr << "**WARNING** ThermalModelBase::NetParticleSusceptibilityFinalByPdg: unknown pdg code " << id2 << std::endl;
1436 return 0.;
1437 }
1438
1439 int i2 = TPS()->PdgToId(-id1);
1440 int j2 = TPS()->PdgToId(-id2);
1441
1442 // Both particles are neutral
1443 if (i2 == -1 && j2 == -1) {
1444 return TwoParticleSusceptibilityFinal(i1, j1);
1445 }
1446
1447 // First particle species is neutral
1448 if (i2 == -1) {
1450 }
1451
1452 // Second particle species is neutral
1453 if (j2 == -1) {
1455 }
1456
1457 // Both particles have antiparticles
1459 }
1460
1462 {
1463 if (!IsFluctuationsCalculated()) {
1464 throw std::runtime_error("ThermalModelBase::PrimordialParticleChargeSusceptibility: fluctuations were not computed beforehand!");
1465 }
1466
1467 return m_PrimChargesCorrel[i][static_cast<int>(chg)] / m_Parameters.T / m_Parameters.T / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
1468 }
1469
1471 {
1472 int i = TPS()->PdgToId(id1);
1473 if (i == -1) {
1474 std::cerr << "**WARNING** ThermalModelBase::PrimordialParticleChargeSusceptibilityByPdg: unknown pdg code " << id1 << std::endl;
1475 return 0.;
1476 }
1477
1479 }
1480
1482 {
1483 int i1 = TPS()->PdgToId(id1);
1484 if (i1 == -1) {
1485 std::cerr << "**WARNING** ThermalModelBase::PrimordialNetParticleChargeSusceptibilityByPdg: unknown pdg code " << id1 << std::endl;
1486 return 0.;
1487 }
1488
1489 int i2 = TPS()->PdgToId(-id1);
1490 if (i2 == -1)
1492
1494 }
1495
1497 {
1498 if (!IsFluctuationsCalculated()) {
1499 throw std::runtime_error("ThermalModelBase::FinalParticleChargeSusceptibility: fluctuations were not computed beforehand!");
1500 }
1501
1502 return m_FinalChargesCorrel[i][static_cast<int>(chg)] / m_Parameters.T / m_Parameters.T / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
1503 }
1504
1506 {
1507 int i = TPS()->PdgToId(id1);
1508 if (i == -1) {
1509 std::cerr << "**WARNING** ThermalModelBase::FinalParticleChargeSusceptibilityByPdg: unknown pdg code " << id1 << std::endl;
1510 return 0.;
1511 }
1512
1513 return FinalParticleChargeSusceptibility(i, chg);
1514 }
1515
1517 {
1518 int i1 = TPS()->PdgToId(id1);
1519 if (i1 == -1) {
1520 std::cerr << "**WARNING** ThermalModelBase::FinalNetParticleChargeSusceptibilityByPdg: unknown pdg code " << id1 << std::endl;
1521 return 0.;
1522 }
1523
1524 int i2 = TPS()->PdgToId(-id1);
1525 if (i2 == -1)
1526 return FinalParticleChargeSusceptibility(i1, chg);
1527
1529 }
1530
1532 {
1533 // Ensure two-particle correlations are calculated first
1534 if (!m_TwoParticleCorrelationsCalculated)
1536
1537 m_Susc.resize(4);
1538 for (int i = 0; i < 4; ++i) m_Susc[i].resize(4);
1539 m_dSuscdT = m_Susc;
1540
1541 for (int i = 0; i < 4; ++i) {
1542 for (int j = 0; j < 4; ++j) {
1543 m_Susc[i][j] = 0.;
1544 m_dSuscdT[i][j] = 0.;
1545 for (size_t k = 0; k < m_PrimCorrel.size(); ++k) {
1546 int c1 = 0;
1547 if (i == 0) c1 = m_TPS->Particles()[k].BaryonCharge();
1548 if (i == 1) c1 = m_TPS->Particles()[k].ElectricCharge();
1549 if (i == 2) c1 = m_TPS->Particles()[k].Strangeness();
1550 if (i == 3) c1 = m_TPS->Particles()[k].Charm();
1551 for (size_t kp = 0; kp < m_PrimCorrel.size(); ++kp) {
1552 int c2 = 0;
1553 if (j == 0) c2 = m_TPS->Particles()[kp].BaryonCharge();
1554 if (j == 1) c2 = m_TPS->Particles()[kp].ElectricCharge();
1555 if (j == 2) c2 = m_TPS->Particles()[kp].Strangeness();
1556 if (j == 3) c2 = m_TPS->Particles()[kp].Charm();
1557 m_Susc[i][j] += c1 * c2 * m_PrimCorrel[k][kp];
1558
1559 if (IsTemperatureDerivativesCalculated())
1560 m_dSuscdT[i][j] += c1 * c2 * m_PrimChi2sdT[k][kp];
1561 }
1562 }
1563 m_Susc[i][j] = m_Susc[i][j] / m_Parameters.T / m_Parameters.T / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
1564 }
1565 }
1566 m_SusceptibilitiesCalculated = true;
1567 }
1568
1570 {
1571 m_ProxySusc.resize(4);
1572 for (int i = 0; i < 4; ++i)
1573 m_ProxySusc[i].resize(4);
1574
1575 // Up to 3, no charm here yet
1576 for (int i = 0; i < 3; ++i) {
1577 for (int j = 0; j < 3; ++j) {
1578 m_ProxySusc[i][j] = 0.;
1579 for (size_t k = 0; k < m_TotalCorrel.size(); ++k) {
1580 if (m_TPS->Particles()[k].IsStable()) {
1581 int c1 = 0;
1582 //if (i == 0) c1 = m_TPS->Particles()[k].BaryonCharge();
1583 if (i == 0) c1 = 1 * (m_TPS->Particles()[k].PdgId() == 2212) - 1 * (m_TPS->Particles()[k].PdgId() == -2212);
1584 if (i == 1) c1 = m_TPS->Particles()[k].ElectricCharge();
1585 //if (i == 1) c1 = 1 * (m_TPS->Particles()[k].PdgId() == 211) - 1 * (m_TPS->Particles()[k].PdgId() == -211);
1586 if (i == 2) c1 = 1 * (m_TPS->Particles()[k].PdgId() == 321) - 1 * (m_TPS->Particles()[k].PdgId() == -321);
1587 if (i == 3) c1 = m_TPS->Particles()[k].Charm();
1588 for (size_t kp = 0; kp < m_TotalCorrel.size(); ++kp) {
1589 if (m_TPS->Particles()[kp].IsStable()) {
1590 int c2 = 0;
1591 //if (j == 0) c2 = m_TPS->Particles()[kp].BaryonCharge();
1592 if (j == 0) c2 = 1 * (m_TPS->Particles()[kp].PdgId() == 2212) - 1 * (m_TPS->Particles()[kp].PdgId() == -2212);
1593 if (j == 1) c2 = m_TPS->Particles()[kp].ElectricCharge();
1594 //if (j == 1) c2 = 1 * (m_TPS->Particles()[kp].PdgId() == 211) - 1 * (m_TPS->Particles()[kp].PdgId() == -211);
1595 if (j == 2) c2 = 1 * (m_TPS->Particles()[kp].PdgId() == 321) - 1 * (m_TPS->Particles()[kp].PdgId() == -321);
1596 if (j == 3) c2 = m_TPS->Particles()[kp].Charm();
1597 m_ProxySusc[i][j] += c1 * c2 * m_TotalCorrel[k][kp];
1598 }
1599 }
1600 }
1601 }
1602 m_ProxySusc[i][j] = m_ProxySusc[i][j] / m_Parameters.T / m_Parameters.T / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
1603 }
1604 }
1605 //std::cerr << "chi2netp/chi2skellam = " << m_ProxySusc[0][0] / (m_densitiestotal[m_TPS->PdgToId(2212)] + m_densitiestotal[m_TPS->PdgToId(-2212)]) * pow(m_Parameters.T * xMath::GeVtoifm(), 3) << std::endl;
1606 //std::cerr << "chi2netpi/chi2skellam = " << m_ProxySusc[1][1] / (m_densitiestotal[m_TPS->PdgToId(211)] + m_densitiestotal[m_TPS->PdgToId(-211)]) * pow(m_Parameters.T * xMath::GeVtoifm(), 3) << std::endl;
1607 }
1608
1610 {
1611 m_PrimChargesCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(4, 0.));
1612 m_FinalChargesCorrel = std::vector< std::vector<double> >(TPS()->ComponentsNumber(), std::vector<double>(4, 0.));
1613
1614 for (size_t i = 0; i < TPS()->ComponentsNumber(); ++i) {
1615 int c1 = 1;
1616 for (int chg = 0; chg < 4; ++chg) {
1617 for (size_t j = 0; j < TPS()->ComponentsNumber(); ++j) {
1618 int c2 = TPS()->Particle(j).ConservedCharge((ConservedCharge::Name)chg);
1619 m_PrimChargesCorrel[i][chg] += c1 * c2 * m_PrimCorrel[i][j];
1620 }
1621 }
1622 }
1623
1624 for (size_t i = 0; i < TPS()->ComponentsNumber(); ++i) {
1625 if (m_TPS->Particles()[i].IsStable()) {
1626 int c1 = 1;
1627 for (int chg = 0; chg < 4; ++chg) {
1628 for (size_t j = 0; j < TPS()->ComponentsNumber(); ++j) {
1629 if (m_TPS->Particles()[j].IsStable()) {
1630 int c2 = TPS()->Particle(j).ConservedCharge((ConservedCharge::Name)chg);
1631 m_FinalChargesCorrel[i][chg] += c1 * c2 * m_TotalCorrel[i][j];
1632 }
1633 }
1634 }
1635 }
1636 }
1637 }
1638
1639 std::vector<double> ThermalModelBase::PartialPressures() {
1640 if (!IsCalculated())
1642
1643 std::vector<double> ret(5, 0.);
1644 for (size_t i = 0; i < TPS()->ComponentsNumber(); ++i) {
1645 const ThermalParticle& tpart = TPS()->Particle(i);
1646 int ind = 0;
1647 if (tpart.BaryonCharge() == 1)
1648 ind = 1;
1649 if (tpart.BaryonCharge() == -1)
1650 ind = 2;
1651 if (tpart.BaryonCharge() > 1)
1652 ind = 3;
1653 if (tpart.BaryonCharge() < -1)
1654 ind = 4;
1655 ret[ind] += m_TPS->Particles()[i].Density(m_Parameters, IdealGasFunctions::Pressure, m_UseWidth, FullIdealChemicalPotential(i));
1656 }
1657
1658 return ret;
1659 }
1660
1662 std::cerr << "**WARNING** " << m_TAG << ": Calculation of fluctuations is not implemented" << std::endl;
1663 }
1664
1666 std::cerr << "**WARNING** " << m_TAG << ": Calculation of temperature derivatives is not implemented" << std::endl;
1667 }
1668
1669 std::vector<double> ThermalModelBase::BroydenEquationsChem::Equations(const std::vector<double>& x)
1670 {
1671 std::vector<double> ret(m_N, 0.);
1672
1673 int i1 = 0;
1674 if (m_THM->ConstrainMuB()) { m_THM->SetBaryonChemicalPotential(x[i1]); i1++; }
1675 if (m_THM->ConstrainMuQ()) { m_THM->SetElectricChemicalPotential(x[i1]); i1++; }
1676 if (m_THM->ConstrainMuS()) { m_THM->SetStrangenessChemicalPotential(x[i1]); i1++; }
1677 if (m_THM->ConstrainMuC()) { m_THM->SetCharmChemicalPotential(x[i1]); i1++; }
1678 m_THM->FillChemicalPotentials();
1679 m_THM->CalculatePrimordialDensities();
1680
1681 i1 = 0;
1682
1683 // Baryon charge
1684 if (m_THM->ConstrainMuB()) {
1685 double fBd = m_THM->CalculateBaryonDensity();
1686 double fSd = m_THM->CalculateEntropyDensity();
1687
1688 ret[i1] = (fBd / fSd - 1. / m_THM->SoverB()) * m_THM->SoverB();
1689
1690 i1++;
1691 }
1692
1693 // Electric charge
1694 if (m_THM->ConstrainMuQ()) {
1695 double fBd = m_THM->CalculateBaryonDensity();
1696 double fQd = m_THM->CalculateChargeDensity();
1697
1698 // Update: remove division by Q/B to allow for charge neutrality
1699 ret[i1] = (fQd / fBd - m_THM->QoverB());
1700 //ret[i1] = (fQd / fBd - m_THM->QoverB()) / m_THM->QoverB();
1701
1702 i1++;
1703 }
1704
1705
1706 // Strangeness
1707 if (m_THM->ConstrainMuS()) {
1708 double fSd = m_THM->CalculateStrangenessDensity();
1709 double fASd = m_THM->CalculateAbsoluteStrangenessDensity();
1710 if (fASd < 1.e-25)
1711 fASd = m_THM->CalculateAbsoluteStrangenessDensityModulo();
1712
1713 ret[i1] = fSd / fASd;
1714
1715 i1++;
1716 }
1717
1718
1719 // Charm
1720 if (m_THM->ConstrainMuC()) {
1721 double fCd = m_THM->CalculateCharmDensity();
1722 double fACd = m_THM->CalculateAbsoluteCharmDensity();
1723 if (fACd < 1.e-25)
1724 fACd = m_THM->CalculateAbsoluteCharmDensityModulo();
1725
1726 ret[i1] = fCd / fACd;
1727
1728 i1++;
1729 }
1730
1731 return ret;
1732 }
1733
1734 std::vector<double> ThermalModelBase::BroydenJacobianChem::Jacobian(const std::vector<double>& x)
1735 {
1736 int i1 = 0;
1737 // Analytic calculations of Jacobian not yet supported if entropy per baryon is involved
1738 if (m_THM->ConstrainMuB()) {
1739 throw std::runtime_error("ThermalModelBase::ConstrainChemicalPotentials: analytic calculation of the Jacobian not supported if muB is constrained");
1740 }
1741
1742 if (m_THM->ConstrainMuQ()) { m_THM->SetElectricChemicalPotential(x[i1]); i1++; }
1743 if (m_THM->ConstrainMuS()) { m_THM->SetStrangenessChemicalPotential(x[i1]); i1++; }
1744 if (m_THM->ConstrainMuC()) { m_THM->SetCharmChemicalPotential(x[i1]); i1++; }
1745 m_THM->FillChemicalPotentials();
1746 m_THM->CalculatePrimordialDensities();
1747
1748 double fBd = m_THM->CalculateBaryonDensity();
1749 double fQd = m_THM->CalculateChargeDensity();
1750 double fSd = m_THM->CalculateStrangenessDensity();
1751 double fASd = m_THM->CalculateAbsoluteStrangenessDensity();
1752 if (fASd < 1.e-25) {
1753 fASd = m_THM->CalculateAbsoluteStrangenessDensityModulo();
1754 }
1755 double fCd = m_THM->CalculateCharmDensity();
1756 double fACd = m_THM->CalculateAbsoluteCharmDensity();
1757 if (fACd < 1.e-25) {
1758 fACd = m_THM->CalculateAbsoluteCharmDensityModulo();
1759 }
1760
1761 vector<double> chi2s;
1762 chi2s.resize(m_THM->Densities().size());
1763 for (size_t i = 0; i < chi2s.size(); ++i) {
1764 chi2s[i] = m_THM->TPS()->Particle(i).chiDimensionfull(2, m_THM->Parameters(), m_THM->UseWidth(), m_THM->ChemicalPotential(i) + m_THM->MuShift(i))
1765 * xMath::GeVtoifm3();
1766 }
1767
1768 int NNN = 0;
1769 if (m_THM->ConstrainMuQ()) NNN++;
1770 if (m_THM->ConstrainMuS()) NNN++;
1771 if (m_THM->ConstrainMuC()) NNN++;
1772 MatrixXd ret(NNN, NNN);
1773
1774 i1 = 0;
1775 // Electric charge derivatives
1776 if (m_THM->ConstrainMuQ()) {
1777 int i2 = 0;
1778
1779 double d1 = 0., d2 = 0.;
1780
1781 if (m_THM->ConstrainMuQ()) {
1782 d1 = 0.;
1783 for (size_t i = 0; i < chi2s.size(); ++i)
1784 d1 += m_THM->TPS()->Particle(i).ElectricCharge() * m_THM->TPS()->Particle(i).ElectricCharge() * chi2s[i];
1785
1786 d2 = 0.;
1787 for (size_t i = 0; i < chi2s.size(); ++i)
1788 d2 += m_THM->TPS()->Particle(i).BaryonCharge() * m_THM->TPS()->Particle(i).ElectricCharge() * chi2s[i];
1789
1790 // Update: remove division by Q/B to allow for charge neutrality
1791 ret(i1, i2) = (d1 / fBd - fQd / fBd / fBd * d2);
1792 //ret(i1, i2) = (d1 / fBd - fQd / fBd / fBd * d2) / m_THM->QoverB();
1793
1794 i2++;
1795 }
1796
1797
1798 if (m_THM->ConstrainMuS()) {
1799 d1 = 0.;
1800 for (size_t i = 0; i < chi2s.size(); ++i)
1801 d1 += m_THM->TPS()->Particle(i).ElectricCharge() * m_THM->TPS()->Particle(i).Strangeness() * chi2s[i];
1802
1803 d2 = 0.;
1804 for (size_t i = 0; i < chi2s.size(); ++i)
1805 d2 += m_THM->TPS()->Particle(i).BaryonCharge() * m_THM->TPS()->Particle(i).Strangeness() * chi2s[i];
1806
1807 // Update: remove division by Q/B to allow for charge neutrality
1808 ret(i1, i2) = (d1 / fBd - fQd / fBd / fBd * d2);
1809 //ret(i1, i2) = (d1 / fBd - fQd / fBd / fBd * d2) / m_THM->QoverB();
1810
1811 i2++;
1812 }
1813
1814
1815 if (m_THM->ConstrainMuC()) {
1816 d1 = 0.;
1817 for (size_t i = 0; i < chi2s.size(); ++i)
1818 d1 += m_THM->TPS()->Particle(i).ElectricCharge() * m_THM->TPS()->Particle(i).Charm() * chi2s[i];
1819
1820 d2 = 0.;
1821 for (size_t i = 0; i < chi2s.size(); ++i)
1822 d2 += m_THM->TPS()->Particle(i).BaryonCharge() * m_THM->TPS()->Particle(i).Charm() * chi2s[i];
1823
1824 // Update: remove division by Q/B to allow for charge neutrality
1825 ret(i1, i2) = (d1 / fBd - fQd / fBd / fBd * d2);
1826 //ret(i1, i2) = (d1 / fBd - fQd / fBd / fBd * d2) / m_THM->QoverB();
1827
1828 i2++;
1829 }
1830
1831 i1++;
1832 }
1833
1834
1835 // Strangeness derivatives
1836 if (m_THM->ConstrainMuS()) {
1837 int i2 = 0;
1838
1839 double d1 = 0., d2 = 0.;
1840
1841 if (m_THM->ConstrainMuQ()) {
1842 d1 = 0.;
1843 for (size_t i = 0; i < chi2s.size(); ++i)
1844 d1 += m_THM->TPS()->Particle(i).Strangeness() * m_THM->TPS()->Particle(i).ElectricCharge() * chi2s[i];
1845
1846 d2 = 0.;
1847 for (size_t i = 0; i < chi2s.size(); ++i)
1848 d2 += m_THM->TPS()->Particle(i).AbsoluteStrangeness() * m_THM->TPS()->Particle(i).ElectricCharge() * chi2s[i];
1849
1850 ret(i1, i2) = d1 / fASd - fSd / fASd / fASd * d2;
1851
1852 i2++;
1853 }
1854
1855
1856 if (m_THM->ConstrainMuS()) {
1857 d1 = 0.;
1858 for (size_t i = 0; i < chi2s.size(); ++i)
1859 d1 += m_THM->TPS()->Particle(i).Strangeness() * m_THM->TPS()->Particle(i).Strangeness() * chi2s[i];
1860
1861 d2 = 0.;
1862 for (size_t i = 0; i < chi2s.size(); ++i)
1863 d2 += m_THM->TPS()->Particle(i).AbsoluteStrangeness() * m_THM->TPS()->Particle(i).Strangeness() * chi2s[i];
1864
1865 ret(i1, i2) = d1 / fASd - fSd / fASd / fASd * d2;
1866
1867 i2++;
1868 }
1869
1870
1871 if (m_THM->ConstrainMuC()) {
1872 d1 = 0.;
1873 for (size_t i = 0; i < chi2s.size(); ++i)
1874 d1 += m_THM->TPS()->Particle(i).Strangeness() * m_THM->TPS()->Particle(i).Charm() * chi2s[i];
1875
1876 d2 = 0.;
1877 for (size_t i = 0; i < chi2s.size(); ++i)
1878 d2 += m_THM->TPS()->Particle(i).AbsoluteStrangeness() * m_THM->TPS()->Particle(i).Charm() * chi2s[i];
1879
1880 ret(i1, i2) = d1 / fASd - fSd / fASd / fASd * d2;
1881
1882 i2++;
1883 }
1884
1885 i1++;
1886 }
1887
1888
1889 // Charm derivatives
1890 if (m_THM->ConstrainMuC()) {
1891 int i2 = 0;
1892
1893 double d1 = 0., d2 = 0.;
1894
1895 if (m_THM->ConstrainMuQ()) {
1896 d1 = 0.;
1897 for (size_t i = 0; i < chi2s.size(); ++i)
1898 d1 += m_THM->TPS()->Particle(i).Charm() * m_THM->TPS()->Particle(i).ElectricCharge() * chi2s[i];
1899
1900 d2 = 0.;
1901 for (size_t i = 0; i < chi2s.size(); ++i)
1902 d2 += m_THM->TPS()->Particle(i).AbsoluteCharm() * m_THM->TPS()->Particle(i).ElectricCharge() *chi2s[i];
1903
1904 ret(i1, i2) = d1 / fACd - fCd / fACd / fACd * d2;
1905
1906 i2++;
1907 }
1908
1909
1910 if (m_THM->ConstrainMuS()) {
1911 d1 = 0.;
1912 for (size_t i = 0; i < chi2s.size(); ++i)
1913 d1 += m_THM->TPS()->Particle(i).Charm() * m_THM->TPS()->Particle(i).Strangeness() * chi2s[i];
1914
1915 d2 = 0.;
1916 for (size_t i = 0; i < chi2s.size(); ++i)
1917 d2 += m_THM->TPS()->Particle(i).AbsoluteCharm() * m_THM->TPS()->Particle(i).Strangeness() * chi2s[i];
1918
1919 ret(i1, i2) = d1 / fACd - fCd / fACd / fACd * d2;
1920
1921 i2++;
1922 }
1923
1924
1925 if (m_THM->ConstrainMuC()) {
1926 d1 = 0.;
1927 for (size_t i = 0; i < chi2s.size(); ++i)
1928 d1 += m_THM->TPS()->Particle(i).Charm() * m_THM->TPS()->Particle(i).Charm() * chi2s[i];
1929
1930 d2 = 0.;
1931 for (size_t i = 0; i < chi2s.size(); ++i)
1932 d2 += m_THM->TPS()->Particle(i).AbsoluteCharm() * m_THM->TPS()->Particle(i).Charm() * chi2s[i];
1933
1934 ret(i1, i2) = d1 / fACd - fCd / fACd / fACd * d2;
1935
1936 i2++;
1937 }
1938
1939 i1++;
1940 }
1941
1942 std::vector<double> retVec(NNN*NNN, 0);
1943 for (int i = 0; i < NNN; ++i)
1944 for (int j = 0; j < NNN; ++j)
1945 retVec[i*NNN + j] = ret(i, j);
1946
1947
1948 return retVec;
1949 }
1950
1951 std::vector<double> ThermalModelBase::BroydenChem::Solve(const std::vector<double>& x0, BroydenSolutionCriterium * solcrit, int max_iterations)
1952 {
1953 if (m_Equations == NULL) {
1954 throw std::runtime_error("Broyden::Solve: Equations to solve not specified!");
1955 }
1956
1957 int NNN = 0;
1958 std::vector<double> xcur;
1959 if (m_THM->ConstrainMuB()) { xcur.push_back(x0[0]); NNN++; }
1960 if (m_THM->ConstrainMuQ()) { xcur.push_back(x0[1]); NNN++; }
1961 if (m_THM->ConstrainMuS()) { xcur.push_back(x0[2]); NNN++; }
1962 if (m_THM->ConstrainMuC()) { xcur.push_back(x0[3]); NNN++; }
1963 if (NNN == 0) {
1964 m_THM->FillChemicalPotentials();
1965 return xcur;
1966 }
1967
1968 m_Equations->SetDimension(NNN);
1969
1970 m_MaxIterations = max_iterations;
1971
1972 BroydenSolutionCriterium *SolutionCriterium = solcrit;
1973 bool UseDefaultSolutionCriterium = false;
1974 if (SolutionCriterium == NULL) {
1975 SolutionCriterium = new BroydenSolutionCriterium(TOL);
1976 UseDefaultSolutionCriterium = true;
1977 }
1978
1979 BroydenJacobian *JacobianInUse = m_Jacobian;
1980 bool UseDefaultJacobian = false;
1981 if (JacobianInUse == NULL || m_THM->ConstrainMuB()) {
1982 JacobianInUse = new BroydenJacobian(m_Equations);
1983 UseDefaultJacobian = true;
1984 }
1985 m_Iterations = 0;
1986 double &maxdiff = m_MaxDifference;
1987 int N = m_Equations->Dimension();
1988
1989
1990
1991 std::vector<double> tmpvec, xdeltavec = xcur;
1992 VectorXd xold(N), xnew(N), xdelta(N);
1993 VectorXd fold(N), fnew(N), fdelta(N);
1994
1995 xold = VectorXd::Map(&xcur[0], xcur.size());
1996
1997 MatrixXd Jac = Eigen::Map< Matrix<double, Dynamic, Dynamic, RowMajor> >(&JacobianInUse->Jacobian(xcur)[0], N, N);
1998
1999 bool constrmuB = m_THM->ConstrainMuB();
2000 bool constrmuQ = m_THM->ConstrainMuQ();
2001 bool constrmuS = m_THM->ConstrainMuS();
2002 bool constrmuC = m_THM->ConstrainMuC();
2003 bool repeat = false;
2004 NNN = 0;
2005 if (m_THM->ConstrainMuB()) {
2006 for (int j = 0; j < Jac.rows(); ++j)
2007 if (Jac(NNN, j) > 1.e8) { repeat = true; m_THM->ConstrainMuB(false); }
2008 double S = m_THM->CalculateEntropyDensity();
2009 double nB = m_THM->CalculateBaryonDensity();
2010 if (abs(S) < 1.e-25 || abs(nB) < 1.e-25) { repeat = true; m_THM->ConstrainMuB(false); }
2011 NNN++;
2012 }
2013 if (m_THM->ConstrainMuQ()) {
2014 for (int j = 0; j < Jac.rows(); ++j)
2015 if (Jac(NNN, j) > 1.e8) { repeat = true; m_THM->ConstrainMuQ(false); }
2016 double nQ = m_THM->CalculateChargeDensity();
2017 double nB = m_THM->CalculateBaryonDensity();
2018 if (abs(nQ) < 1.e-25 || abs(nB) < 1.e-25) { repeat = true; m_THM->ConstrainMuQ(false); }
2019 NNN++;
2020 }
2021 if (m_THM->ConstrainMuS()) {
2022 for (int j = 0; j < Jac.rows(); ++j)
2023 if (Jac(NNN, j) > 1.e8) { repeat = true; m_THM->ConstrainMuS(false); }
2024
2025 double nS = m_THM->CalculateAbsoluteStrangenessDensity();
2026 if (abs(nS) < 1.e-25) { nS = m_THM->CalculateAbsoluteStrangenessDensityModulo(); }
2027 if (abs(nS) < 1.e-25) { repeat = true; m_THM->ConstrainMuS(false); }
2028 NNN++;
2029 }
2030 if (m_THM->ConstrainMuC()) {
2031 for (int j = 0; j < Jac.rows(); ++j)
2032 if (Jac(NNN, j) > 1.e8) { repeat = true; m_THM->ConstrainMuC(false); }
2033 double nC = m_THM->CalculateAbsoluteCharmDensity();
2034 if (abs(nC) < 1.e-25) { nC = m_THM->CalculateAbsoluteCharmDensityModulo(); }
2035 if (abs(nC) < 1.e-25) { repeat = true; m_THM->ConstrainMuC(false); }
2036 NNN++;
2037 }
2038 if (repeat) {
2039 std::vector<double> ret = Solve(x0, solcrit, max_iterations);
2040 m_THM->ConstrainMuQ(constrmuB);
2041 m_THM->ConstrainMuQ(constrmuQ);
2042 m_THM->ConstrainMuS(constrmuS);
2043 m_THM->ConstrainMuC(constrmuC);
2044 return ret;
2045 }
2046
2047 if (Jac.determinant() == 0.0)
2048 {
2049 std::cerr << "**WARNING** Singular Jacobian in Broyden::Solve" << std::endl;
2050 return xcur;
2051 }
2052
2053 MatrixXd Jinv = Jac.inverse();
2054 tmpvec = m_Equations->Equations(xcur);
2055 fold = VectorXd::Map(&tmpvec[0], tmpvec.size());
2056
2057 for (m_Iterations = 1; m_Iterations < max_iterations; ++m_Iterations) {
2058 xnew = xold - Jinv * fold;
2059
2060 VectorXd::Map(&xcur[0], xcur.size()) = xnew;
2061
2062 tmpvec = m_Equations->Equations(xcur);
2063 fnew = VectorXd::Map(&tmpvec[0], tmpvec.size());
2064
2065
2066 maxdiff = 0.;
2067 for (size_t i = 0; i < xcur.size(); ++i) {
2068 maxdiff = std::max(maxdiff, fabs(fnew[i]));
2069 }
2070
2071 xdelta = xnew - xold;
2072 fdelta = fnew - fold;
2073
2074 VectorXd::Map(&xdeltavec[0], xdeltavec.size()) = xdelta;
2075
2076 if (SolutionCriterium->IsSolved(xcur, tmpvec, xdeltavec))
2077 break;
2078
2079 if (!m_UseNewton) // Use Broyden's method
2080 {
2081
2082 double norm = 0.;
2083 for (int i = 0; i < N; ++i)
2084 for (int j = 0; j < N; ++j)
2085 norm += xdelta[i] * Jinv(i, j) * fdelta[j];
2086 VectorXd p1(N);
2087 p1 = (xdelta - Jinv * fdelta);
2088 for (int i = 0; i < N; ++i) p1[i] *= 1. / norm;
2089 Jinv = Jinv + (p1 * xdelta.transpose()) * Jinv;
2090 }
2091 else // Use Newton's method
2092 {
2093 Jac = Eigen::Map< Matrix<double, Dynamic, Dynamic, RowMajor> >(&JacobianInUse->Jacobian(xcur)[0], N, N);
2094 Jinv = Jac.inverse();
2095 }
2096
2097 xold = xnew;
2098 fold = fnew;
2099 }
2100
2101 if (m_Iterations == max_iterations) {
2102 std::cerr << "**WARNING** Reached maximum number of iterations in Broyden procedure" << std::endl;
2103 }
2104
2105 if (UseDefaultSolutionCriterium) {
2106 delete SolutionCriterium;
2107 SolutionCriterium = NULL;
2108 }
2109 if (UseDefaultJacobian) {
2110 delete JacobianInUse;
2111 JacobianInUse = NULL;
2112 }
2113 return xcur;
2114 }
2115
2116
2117 ThermalModelBase::BroydenEquationsChemTotals::BroydenEquationsChemTotals(const std::vector<int>& vConstr, const std::vector<int>& vType, const std::vector<double>& vTotals, ThermalModelBase * model) :
2118 BroydenEquations(), m_Constr(vConstr), m_Type(vType), m_Totals(vTotals), m_THM(model)
2119 {
2120 m_N = 0;
2121 for (size_t i = 0; i < m_Constr.size(); ++i)
2122 m_N += m_Constr[i];
2123 }
2124
2125 std::vector<double> ThermalModelBase::BroydenEquationsChemTotals::Equations(const std::vector<double>& x)
2126 {
2127 std::vector<double> ret(m_N, 0.);
2128
2129 int i1 = 0;
2130 for (int i = 0; i < 4; ++i) {
2131 if (m_Constr[i]) {
2132 if (i == 0) m_THM->SetBaryonChemicalPotential(x[i1]);
2133 if (i == 1) m_THM->SetElectricChemicalPotential(x[i1]);
2134 if (i == 2) m_THM->SetStrangenessChemicalPotential(x[i1]);
2135 if (i == 3) m_THM->SetCharmChemicalPotential(x[i1]);
2136 i1++;
2137 }
2138 }
2139 m_THM->FillChemicalPotentials();
2140 m_THM->CalculatePrimordialDensities();
2141
2142 vector<double> dens(4, 0.), absdens(4, 0.);
2143 if (m_Constr[0]) {
2144 dens[0] = m_THM->CalculateBaryonDensity();
2145 absdens[0] = m_THM->CalculateAbsoluteBaryonDensity();
2146 }
2147 if (m_Constr[1]) {
2148 dens[1] = m_THM->CalculateChargeDensity();
2149 absdens[1] = m_THM->CalculateAbsoluteChargeDensity();
2150 }
2151 if (m_Constr[2]) {
2152 dens[2] = m_THM->CalculateStrangenessDensity();
2153 absdens[2] = m_THM->CalculateAbsoluteStrangenessDensity();
2154 if (absdens[2] < 1.e-25) {
2155 absdens[2] = m_THM->CalculateAbsoluteStrangenessDensityModulo();
2156 }
2157 }
2158 if (m_Constr[3]) {
2159 dens[3] = m_THM->CalculateCharmDensity();
2160 absdens[3] = m_THM->CalculateAbsoluteCharmDensity();
2161 if (absdens[3] < 1.e-25) {
2162 absdens[3] = m_THM->CalculateAbsoluteCharmDensityModulo();
2163 }
2164 }
2165
2166 i1 = 0;
2167
2168 for (int i = 0; i < 4; ++i) {
2169 if (m_Constr[i]) {
2170 if (m_Type[i] == 0)
2171 ret[i1] = (dens[i] * m_THM->Parameters().V - m_Totals[i]) / m_Totals[i];
2172 else
2173 ret[i1] = dens[i] / absdens[i];
2174 i1++;
2175 }
2176 }
2177
2178 return ret;
2179 }
2180
2181 std::vector<double> ThermalModelBase::BroydenJacobianChemTotals::Jacobian(const std::vector<double>& x)
2182 {
2183 int NNN = 0;
2184 for (int i = 0; i < 4; ++i) NNN += m_Constr[i];
2185
2186 int i1 = 0;
2187
2188 for (int i = 0; i < 4; ++i) {
2189 if (m_Constr[i]) {
2190 if (i == 0) m_THM->SetBaryonChemicalPotential(x[i1]);
2191 if (i == 1) m_THM->SetElectricChemicalPotential(x[i1]);
2192 if (i == 2) m_THM->SetStrangenessChemicalPotential(x[i1]);
2193 if (i == 3) m_THM->SetCharmChemicalPotential(x[i1]);
2194 i1++;
2195 }
2196 }
2197
2198 m_THM->FillChemicalPotentials();
2199 m_THM->CalculatePrimordialDensities();
2200
2201 vector<double> dens(4, 0.), absdens(4, 0.);
2202 if (m_Constr[0]) {
2203 dens[0] = m_THM->CalculateBaryonDensity();
2204 absdens[0] = m_THM->CalculateAbsoluteBaryonDensity();
2205 }
2206 if (m_Constr[1]) {
2207 dens[1] = m_THM->CalculateChargeDensity();
2208 absdens[1] = m_THM->CalculateAbsoluteChargeDensity();
2209 }
2210 if (m_Constr[2]) {
2211 dens[2] = m_THM->CalculateStrangenessDensity();
2212 absdens[2] = m_THM->CalculateAbsoluteStrangenessDensity();
2213 if (absdens[2] < 1.e-25) {
2214 absdens[2] = m_THM->CalculateAbsoluteStrangenessDensityModulo();
2215 }
2216 }
2217 if (m_Constr[3]) {
2218 dens[3] = m_THM->CalculateCharmDensity();
2219 absdens[3] = m_THM->CalculateAbsoluteCharmDensity();
2220 if (absdens[3] < 1.e-25) {
2221 absdens[3] = m_THM->CalculateAbsoluteCharmDensityModulo();
2222 }
2223 }
2224
2225 vector<double> chi2s;
2226 chi2s.resize(m_THM->Densities().size());
2227 for (size_t i = 0; i < chi2s.size(); ++i) {
2228 chi2s[i] = m_THM->TPS()->Particle(i).chiDimensionfull(2, m_THM->Parameters(), m_THM->UseWidth(), m_THM->ChemicalPotential(i) + m_THM->MuShift(i))
2229 * xMath::GeVtoifm3();
2230 }
2231
2232 vector< vector<double> > deriv(4, vector<double>(4)), derivabs(4, vector<double>(4));
2233 for (int i = 0; i < 4; ++i)
2234 for (int j = 0; j < 4; ++j) {
2235 deriv[i][j] = 0.;
2236 for (size_t part = 0; part < chi2s.size(); ++part)
2237 deriv[i][j] += m_THM->TPS()->Particles()[part].GetCharge(i) * m_THM->TPS()->Particles()[part].GetCharge(j) * chi2s[part];
2238
2239 derivabs[i][j] = 0.;
2240 for (size_t part = 0; part < chi2s.size(); ++part)
2241 derivabs[i][j] += m_THM->TPS()->Particles()[part].GetAbsCharge(i) * m_THM->TPS()->Particles()[part].GetCharge(j) * chi2s[part];
2242 }
2243
2244
2245 MatrixXd ret(NNN, NNN);
2246
2247 i1 = 0;
2248
2249 for (int i = 0; i < 4; ++i) {
2250 if (m_Constr[i]) {
2251 int i2 = 0;
2252 for (int j = 0; j < 4; ++j)
2253 if (m_Constr[j]) {
2254 ret(i1, i2) = 0.;
2255 if (m_Type[i] == 0)
2256 ret(i1, i2) = deriv[i][j] * m_THM->Parameters().V / m_Totals[i];
2257 else
2258 ret(i1, i2) = deriv[i][j] / absdens[i] - dens[i] / absdens[i] / absdens[i] * derivabs[i][j];
2259 i2++;
2260 }
2261 i1++;
2262 }
2263 }
2264
2265 std::vector<double> retVec(NNN*NNN, 0);
2266 for (int i = 0; i < NNN; ++i)
2267 for (int j = 0; j < NNN; ++j)
2268 retVec[i*NNN + j] = ret(i, j);
2269
2270
2271 return retVec;
2272 }
2273
2274 void ThermalModelBase::SetDensityModelForParticleSpecies(int i, GeneralizedDensity *density_model)
2275 {
2276 if (i >= 0 && i < ComponentsNumber()) {
2277 TPS()->Particle(i).SetGeneralizedDensity(density_model);
2278 } else {
2279 std::cerr << "**WARNING** ThermalModelBase::SetDensityModelForParticleSpecies(): Particle id " << i << " is outside the range!" << std::endl;
2280 }
2281 }
2282
2283 void ThermalModelBase::SetDensityModelForParticleSpeciesByPdg(long long PDGID, GeneralizedDensity *density_model)
2284 {
2285 int id = TPS()->PdgToId(PDGID);
2286 if (id != -1) {
2287 TPS()->Particle(id).SetGeneralizedDensity(density_model);
2288 } else {
2289 std::cerr << "**WARNING** ThermalModelBase::SetDensityModelForParticleSpeciesByPdg(): Pdg code " << PDGID << " is outside the range!" << std::endl;
2290 }
2291 }
2292
2293 void ThermalModelBase::ClearDensityModels() {
2294 for (auto& particle : TPS()->Particles())
2295 particle.ClearGeneralizedDensity();
2296 }
2297
2298 void ThermalModelBase::SetMagneticField(double B, int lmax) {
2299 m_IGFExtraConfig.MagneticField.B = B;
2300 if (lmax >= 0)
2301 m_IGFExtraConfig.MagneticField.lmax = lmax;
2302 for (auto& particle : TPS()->Particles())
2303 particle.SetMagneticField(B, m_IGFExtraConfig.MagneticField.lmax);
2304 RecomputeThresholdsDueToMagneticField();
2305 ResetCalculatedFlags();
2306 }
2307
2308 void ThermalModelBase::RecomputeThresholdsDueToMagneticField() {
2309 const double &B = m_IGFExtraConfig.MagneticField.B;
2310 for (int ipart = 0; ipart < TPS()->ComponentsNumber(); ++ipart) {
2311 ThermalParticle &part = TPS()->Particle(ipart);
2312 if (!part.ZeroWidthEnforced() || part.ElectricCharge()==0) {
2313 if (B == 0.) {
2314 part.CalculateAndSetDynamicalThreshold();
2315 part.FillCoefficientsDynamical();
2316 } else if (!part.ZeroWidthEnforced()) {
2317 double szmax = (part.Degeneracy() - 1.) / 2.;
2318 if (szmax > 0.5) {
2319 double mmin = sqrt(2. * abs(part.ElectricCharge()) * B * (szmax - 0.5));
2320 part.CalculateAndSetDynamicalThreshold();
2321 if (mmin > part.DecayThresholdMassDynamical()) {
2322 part.SetDecayThresholdMassDynamical(mmin);
2323 }
2324 part.FillCoefficientsDynamical();
2325 }
2326 }
2327 }
2328 }
2329 }
2330
2331 void ThermalModelBase::ClearMagneticField() {
2332 m_IGFExtraConfig.MagneticField.B = 0.;
2333 for (auto& particle : TPS()->Particles())
2334 particle.ClearMagneticField();
2335 ResetCalculatedFlags();
2336 }
2337
2338 double ThermalModelBase::Susc(ConservedCharge::Name i, ConservedCharge::Name j) const {
2339 assert(IsSusceptibilitiesCalculated());
2340 return m_Susc[i][j];
2341 }
2342
2343 double ThermalModelBase::dSuscdT(ConservedCharge::Name i, ConservedCharge::Name j) const {
2344 assert(IsSusceptibilitiesCalculated() && IsTemperatureDerivativesCalculated());
2345 return m_dSuscdT[i][j];
2346 }
2347
2348 double ThermalModelBase::ProxySusc(ConservedCharge::Name i, ConservedCharge::Name j) const {
2349 assert(IsFluctuationsCalculated());
2350 return m_ProxySusc[i][j];
2351 }
2352
2353 double ThermalModelBase::GetdndT(int i) const {
2354 if (IsTemperatureDerivativesCalculated() && i >= 0 && i < ComponentsNumber())
2355 return m_dndT[i];
2356 else {
2357 std::cerr << "**WARNING** ThermalModelBase::GetdndT(): Temperature derivatives are not calculated or particle id " << i << " is outside the range!" << std::endl;
2358 return 0.;
2359 }
2360 return 0.;
2361 }
2362
2364 double ret = 0.;
2365 for (size_t k = 0; k < m_PrimCorrel.size(); ++k) {
2366 int c1 = m_TPS->Particles()[k].ConservedCharge(i);
2367 for (size_t kp = 0; kp < m_PrimCorrel.size(); ++kp) {
2368 int c2 = m_TPS->Particles()[kp].ConservedCharge(j);
2369 ret += c1 * c2 * m_PrimCorrel[k][kp];
2370 }
2371 }
2372 return ret / xMath::GeVtoifm() / xMath::GeVtoifm() / xMath::GeVtoifm();
2373 }
2374
2375 double ThermalModelBase::CalculateHeatCapacityMu()
2376 {
2377 double dedT = CalculateEnergyDensityDerivativeT(); // fm^-3
2378
2379 // T * ds/dT = (de/dT - mu_i d n_i / d T)
2380 double ret = dedT;
2381 for(int i = 0; i < TPS()->ComponentsNumber(); ++i) {
2382 ret -= m_Chem[i] * m_dndT[i];
2383 }
2384 return ret;
2385 }
2386
2387 // Auxiliary function to compute the susceptibility matrix
2388 MatrixXd GetSusceptibilityMatrix(ThermalModelBase* model, const vector<int>& ConservedDensities) {
2389 int N = 0;
2390 for (int i = 0; i < 4; ++i)
2391 if (ConservedDensities[i] == 1)
2392 N++;
2393
2394 assert(N > 0);
2395
2396 MatrixXd ret(N, N);
2397
2398 // Pi(i,j) = d P / dmui dmuj
2399 {
2400 int i1 = 0;
2401 for(int i = 0; i < 4; ++i) {
2402 if (ConservedDensities[i] == 0)
2403 continue;
2404 int i2 = 0;
2405 for(int j = 0; j < 4; ++j) {
2406 if (ConservedDensities[j] == 0)
2407 continue;
2409 i2++;
2410 }
2411 i1++;
2412 }
2413 }
2414
2415 return ret;
2416 }
2417
2418 // Auxiliary function to compute the Hessian matrix of pressure
2419 MatrixXd GetPressureHessianMatrix(ThermalModelBase* model, const vector<int>& ConservedDensities) {
2420 int N = 1;
2421 for (int i = 0; i < 4; ++i)
2422 if (ConservedDensities[i] == 1)
2423 N++;
2424
2425 MatrixXd ret(N, N);
2426
2427 // Compute the Hessian matrix of pressure
2428 // Pi(0,0) = d^2 P / d T^2 = ds/dT
2429 ret(0, 0) = model->CalculateEntropyDensityDerivativeT(); // fm^-3 GeV^-1
2430
2431 // Pi(0,i) = Pi(i,0) = d^2 P / dT dmui = d ni / d T
2432 {
2433 int i1 = 0;
2434 for(int i = 0; i < 4; ++i) {
2435 if (ConservedDensities[i] == 0)
2436 continue;
2437 ret(0, i1 + 1) = ret(i1 + 1, 0) = model->ConservedChargeDensitydT((ConservedCharge::Name)i);
2438 i1++;
2439 }
2440 }
2441
2442 // Pi(i,j) = d^2 P / dmui dmuj
2443 {
2444 int i1 = 0;
2445 for(int i = 0; i < 4; ++i) {
2446 if (ConservedDensities[i] == 0)
2447 continue;
2448 int i2 = 0;
2449 for(int j = 0; j < 4; ++j) {
2450 if (ConservedDensities[j] == 0)
2451 continue;
2453 i2++;
2454 }
2455 i1++;
2456 }
2457 }
2458
2459 return ret;
2460 }
2461
2462 vector<vector<double>> ThermalModelBase::PressureHessian()
2463 {
2464 // Ensure susceptibilities and temperature derivatives are calculated
2465 if (!m_FluctuationsCalculated)
2467 if (!m_TemperatureDerivativesCalculated)
2469
2470 // Use the existing helper with all conserved charges enabled
2471 vector<int> allCharges = {1, 1, 1, 1}; // B, Q, S, C
2472 MatrixXd H_eigen = GetPressureHessianMatrix(this, allCharges);
2473
2474 // Convert Eigen matrix to vector<vector<double>>
2475 int N = H_eigen.rows();
2476 vector<vector<double>> H(N, vector<double>(N, 0.0));
2477 for (int i = 0; i < N; ++i) {
2478 for (int j = 0; j < N; ++j) {
2479 H[i][j] = H_eigen(i, j);
2480 }
2481 }
2482
2483 return H;
2484 }
2485
2486 bool ThermalModelBase::IsThermodynamicallyStable()
2487 {
2488 // Ensure susceptibilities and temperature derivatives are calculated
2489 if (!m_FluctuationsCalculated)
2491 if (!m_TemperatureDerivativesCalculated)
2493
2494 // Get the Hessian matrix using the existing helper
2495 vector<int> allCharges = {1, 1, 1, 1}; // B, Q, S, C
2496 MatrixXd H = GetPressureHessianMatrix(this, allCharges);
2497
2498 // Use SelfAdjointEigenSolver since Hessian is symmetric
2499 Eigen::SelfAdjointEigenSolver<MatrixXd> solver(H);
2500 if (solver.info() != Eigen::Success) {
2501 return false; // Eigenvalue computation failed
2502 }
2503
2504 // Check if all eigenvalues are non-negative (positive semi-definite)
2505 const auto& eigenvalues = solver.eigenvalues();
2506 for (int i = 0; i < eigenvalues.size(); ++i) {
2507 if (eigenvalues(i) < 0.0) {
2508 return false;
2509 }
2510 }
2511
2512 return true;
2513 }
2514
2515 double ThermalModelBase::CalculateAdiabaticSpeedOfSoundSquared(bool rhoBconst, bool rhoQconst, bool rhoSconst, bool rhoCconst) {
2516 double ret = 0.;
2517 const double nanv = std::numeric_limits<double>::quiet_NaN();
2518 const double eps = 1e-14;
2519
2520 double T = Parameters().T;
2521
2522 std::vector<double> mus = {Parameters().muB, Parameters().muQ, Parameters().muS, Parameters().muC};
2523 std::vector<int> ConservedCharges = {static_cast<int>(rhoBconst), static_cast<int>(rhoQconst), static_cast<int>(rhoSconst), static_cast<int>(rhoCconst)};
2524
2525 if (!TPS()->hasBaryons())
2526 ConservedCharges[0] = 0;
2527 if (!TPS()->hasCharged())
2528 ConservedCharges[1] = 0;
2529 if (!TPS()->hasStrange())
2530 ConservedCharges[2] = 0;
2531 if (!TPS()->hasCharmed())
2532 ConservedCharges[3] = 0;
2533
2534 // Drop conserved charge constraints that are vacuous:
2535 // if the absolute density is zero, the constraint is trivially satisfied
2536 // and including it would make the susceptibility/Hessian matrix singular
2537 // (e.g. strangeness at T=0 when mu_S < m_K)
2538 for (int i = 0; i < 4; ++i) {
2539 if (ConservedCharges[i] == 1
2540 && AbsoluteConservedChargeDensity((ConservedCharge::Name)i) < eps)
2541 ConservedCharges[i] = 0;
2542 }
2543
2544 // Zero chemical potentials
2545 {
2546 bool AllMuZero = true;
2547 for(int i = 0; i < ConservedCharges.size(); ++i)
2548 AllMuZero &= (ConservedCharges[i] == 0 || mus[i] == 0.0);
2549 if (AllMuZero) {
2550 if (T == 0.) {
2551 WarnIllDefinedSoundSpeedOnce(
2552 "CalculateAdiabaticSpeedOfSoundSquared", ConservedCharges, T,
2553 "all relevant chemical potentials are zero (0/0 limit at T=0).");
2554 return nanv;
2555 }
2556 // At mu = 0 cs2 = s(T) / e'(T)
2557 // Calculate temperature derivatives if not already
2558 if (!IsTemperatureDerivativesCalculated())
2560 return EntropyDensity() / CalculateEnergyDensityDerivativeT();
2561 //return EntropyDensity() / HeatCapacityMu();
2562 }
2563 }
2564
2565 int Ndens = 0;
2566 for(int i = 0; i < 4; ++i)
2567 if (ConservedCharges[i] == 1)
2568 Ndens++;
2569 if (Ndens == 0) {
2570 WarnIllDefinedSoundSpeedOnce(
2571 "CalculateAdiabaticSpeedOfSoundSquared", ConservedCharges, T,
2572 "no conserved densities selected.");
2573 return nanv;
2574 }
2575
2576 // Compute fluctuations if not already
2577 if (!IsFluctuationsCalculated())
2579
2580 // Zero temperature
2581 if (T == 0.) {
2582
2583 MatrixXd chi2matr = GetSusceptibilityMatrix(this, ConservedCharges);
2584 if (!IsMatrixInvertible(chi2matr)) {
2585 WarnIllDefinedSoundSpeedOnce(
2586 "CalculateAdiabaticSpeedOfSoundSquared", ConservedCharges, T,
2587 "susceptibility matrix is singular at T=0.");
2588 return nanv;
2589 }
2590 MatrixXd chi2inv = chi2matr.inverse();
2591 ret = 0.;
2592 int i1 = 0, i2 = 0;
2593 for(int i = 0; i < 4; ++i) {
2594 if (ConservedCharges[i] == 0)
2595 continue;
2596 i2 = 0;
2597 for(int j = 0; j < 4; ++j) {
2598 if (ConservedCharges[j] == 0)
2599 continue;
2600
2601 auto chg1 = (ConservedCharge::Name)i;
2602 auto chg2 = (ConservedCharge::Name)j;
2603 ret += ConservedChargeDensity(chg1) * ConservedChargeDensity(chg2) * chi2inv(i1, i2);
2604 i2++;
2605 }
2606 i1++;
2607 }
2608
2609 const double ep = EnergyDensity() + Pressure();
2610 if (std::abs(ep) < eps) {
2611 WarnIllDefinedSoundSpeedOnce(
2612 "CalculateAdiabaticSpeedOfSoundSquared", ConservedCharges, T,
2613 "EnergyDensity + Pressure is zero.");
2614 return nanv;
2615 }
2616 ret /= ep;
2617 return ret;
2618 }
2619
2620 // General case
2621
2622 // Calculate temperature derivatives if not already
2623 if (!IsTemperatureDerivativesCalculated())
2625
2626 // double dedT = HeatCapacityMu(); // fm^-3
2627 // Compute the Hessian matrix of pressure
2628 MatrixXd PiMatr = GetPressureHessianMatrix(this, ConservedCharges);
2629
2630 // Compute the inverse of the Hessian matrix
2631 if (!IsMatrixInvertible(PiMatr)) {
2632 WarnIllDefinedSoundSpeedOnce(
2633 "CalculateAdiabaticSpeedOfSoundSquared", ConservedCharges, T,
2634 "pressure Hessian matrix is singular.");
2635 return nanv;
2636 }
2637 MatrixXd PiMatrInv = PiMatr.inverse();
2638 // Prepare the vector x
2639 double s = EntropyDensity();
2640 vector<double> x = {s};
2641 for(int i = 0; i < ConservedCharges.size(); ++i)
2642 if (ConservedCharges[i] == 1)
2643 x.push_back(ConservedChargeDensity((ConservedCharge::Name)i));
2644
2645 ret = 0.;
2646 for(int i = 0; i < x.size(); ++i)
2647 for(int j = 0; j < x.size(); ++j)
2648 ret += x[i] * x[j] * PiMatrInv(i,j);
2649 const double ep = EnergyDensity() + Pressure();
2650 if (std::abs(ep) < eps) {
2651 WarnIllDefinedSoundSpeedOnce(
2652 "CalculateAdiabaticSpeedOfSoundSquared", ConservedCharges, T,
2653 "EnergyDensity + Pressure is zero.");
2654 return nanv;
2655 }
2656 ret /= ep;
2657
2658 return ret;
2659 }
2660
2661 double ThermalModelBase::CalculateIsothermalSpeedOfSoundSquared(bool rhoBconst, bool rhoQconst, bool rhoSconst, bool rhoCconst) {
2662 double ret = 0.;
2663 const double nanv = std::numeric_limits<double>::quiet_NaN();
2664 const double eps = 1e-14;
2665
2666 double T = Parameters().T;
2667
2668 std::vector<double> mus = {Parameters().muB, Parameters().muQ, Parameters().muS, Parameters().muC};
2669 std::vector<int> ConservedCharges = {static_cast<int>(rhoBconst), static_cast<int>(rhoQconst), static_cast<int>(rhoSconst), static_cast<int>(rhoCconst)};
2670
2671 if (!TPS()->hasBaryons())
2672 ConservedCharges[0] = 0;
2673 if (!TPS()->hasCharged())
2674 ConservedCharges[1] = 0;
2675 if (!TPS()->hasStrange())
2676 ConservedCharges[2] = 0;
2677 if (!TPS()->hasCharmed())
2678 ConservedCharges[3] = 0;
2679
2680 // Drop conserved charge constraints that are vacuous:
2681 // if the absolute density is zero, the constraint is trivially satisfied
2682 // and including it would make the susceptibility matrix singular
2683 // (e.g. strangeness at T=0 when mu_S < m_K)
2684 for (int i = 0; i < 4; ++i) {
2685 if (ConservedCharges[i] == 1
2686 && AbsoluteConservedChargeDensity((ConservedCharge::Name)i) < eps)
2687 ConservedCharges[i] = 0;
2688 }
2689
2690 int Ndens = 0;
2691 for (int i = 0; i < 4; ++i)
2692 if (ConservedCharges[i] == 1)
2693 Ndens++;
2694 if (Ndens == 0) {
2695 WarnIllDefinedSoundSpeedOnce(
2696 "CalculateIsothermalSpeedOfSoundSquared", ConservedCharges, T,
2697 "no conserved densities selected.");
2698 return nanv;
2699 }
2700
2701 // If all chemical potentials are zero, c_T is undefined (return 0.)
2702 {
2703 bool AllMuZero = true;
2704 for(int i = 0; i < ConservedCharges.size(); ++i)
2705 AllMuZero &= (ConservedCharges[i] == 0 || mus[i] == 0.0);
2706 if (AllMuZero) {
2707 return 0.;
2708 }
2709 }
2710
2711 auto chi2Matr = GetSusceptibilityMatrix(this, ConservedCharges);
2712 if (!IsMatrixInvertible(chi2Matr)) {
2713 WarnIllDefinedSoundSpeedOnce(
2714 "CalculateIsothermalSpeedOfSoundSquared", ConservedCharges, T,
2715 "susceptibility matrix is singular.");
2716 return nanv;
2717 }
2718 // Compute the inverse of the susceptibility matrix
2719 MatrixXd chi2inv= chi2Matr.inverse();
2720
2721 double numerator = 0.;
2722 double denominator = 0.;
2723 int i1 = 0, i2 = 0;
2724 for(int i = 0; i < 4; ++i) {
2725 if (ConservedCharges[i] == 0)
2726 continue;
2727 denominator += mus[i] * ConservedChargeDensity((ConservedCharge::Name)i);
2728 i2 = 0;
2729 for(int j = 0; j < 4; ++j) {
2730 if (ConservedCharges[j] == 0)
2731 continue;
2732
2733 auto chg1 = (ConservedCharge::Name)i;
2734 auto chg2 = (ConservedCharge::Name)j;
2735
2736 numerator += ConservedChargeDensity(chg1) * ConservedChargeDensity(chg2) * chi2inv(i1, i2);
2737 denominator += T * ConservedChargeDensitydT(chg1) * chi2inv(i1, i2) * ConservedChargeDensity(chg2);
2738
2739 ret += ConservedChargeDensity(chg1) * ConservedChargeDensity(chg2) * chi2inv(i1, i2);
2740 i2++;
2741 }
2742 i1++;
2743 }
2744
2745 if (std::abs(denominator) < eps) {
2746 WarnIllDefinedSoundSpeedOnce(
2747 "CalculateIsothermalSpeedOfSoundSquared", ConservedCharges, T,
2748 "denominator is zero.");
2749 return nanv;
2750 }
2751 return numerator / denominator;
2752 }
2753
2754 double ThermalModelBase::CalculateHeatCapacity(bool rhoBconst, bool rhoQconst, bool rhoSconst, bool rhoCconst) {
2755 double T = Parameters().T;
2756
2757 if (T == 0.) {
2758 return 0.;
2759 }
2760
2761 std::vector<double> mus = {Parameters().muB, Parameters().muQ, Parameters().muS, Parameters().muC};
2762 std::vector<int> ConservedCharges = {static_cast<int>(rhoBconst), static_cast<int>(rhoQconst), static_cast<int>(rhoSconst), static_cast<int>(rhoCconst)};
2763
2764 if (!TPS()->hasBaryons())
2765 ConservedCharges[0] = 0;
2766 if (!TPS()->hasCharged())
2767 ConservedCharges[1] = 0;
2768 if (!TPS()->hasStrange())
2769 ConservedCharges[2] = 0;
2770 if (!TPS()->hasCharmed())
2771 ConservedCharges[3] = 0;
2772
2773 // Drop conserved charge constraints that are vacuous:
2774 // if the absolute density is zero, the constraint is trivially satisfied
2775 // and including it would make the Hessian matrix singular
2776 // (e.g. strangeness at T=0 when mu_S < m_K)
2777 {
2778 const double eps = 1e-14;
2779 for (int i = 0; i < 4; ++i) {
2780 if (ConservedCharges[i] == 1
2781 && AbsoluteConservedChargeDensity((ConservedCharge::Name)i) < eps)
2782 ConservedCharges[i] = 0;
2783 }
2784 }
2785
2786 // Calculate the numerator
2787 double TdsdT = HeatCapacityMu();
2788
2789 // If all chemical potentials are zero, return TdsT
2790 {
2791 bool AllMuZero = true;
2792 for(int i = 0; i < ConservedCharges.size(); ++i)
2793 AllMuZero &= (ConservedCharges[i] == 0 || mus[i] == 0.0);
2794 if (AllMuZero) {
2795 return TdsdT;
2796 }
2797 }
2798
2799 if (!IsFluctuationsCalculated())
2801
2802 // Calculate the denominator
2803 double denominator = 1.;
2804 auto PiMatr = GetPressureHessianMatrix(this, ConservedCharges);
2805 auto PiMatrInv = PiMatr.inverse();
2806 int N = PiMatr.rows();
2807 for(int i = 1; i < N; ++i) {
2808 denominator -= PiMatr(0, i) * PiMatrInv(i, 0);
2809 }
2810
2811 double heatCapacity = TdsdT / denominator;
2812 return heatCapacity;
2813 }
2814
2815} // namespace thermalfist
map< string, double > params
Contains some helper functions.
Sub-class where it is determined whether the required accuracy is achieved in the Broyden's method.
Definition Broyden.h:144
Abstract class which defines the system of non-linear equations to be solved by the Broyden's method.
Definition Broyden.h:32
Class implementing the Broyden method to solve a system of non-linear equations.
Definition Broyden.h:132
virtual std::vector< double > Solve(const std::vector< double > &x0, BroydenSolutionCriterium *solcrit=NULL, int max_iterations=MAX_ITERS)
Definition Broyden.cpp:83
int Iterations() const
Definition Broyden.h:229
int MaxIterations() const
Definition Broyden.h:234
Class which implements calculation of the Jacobian needed for the Broyden's method.
Definition Broyden.h:77
static bool PrintDisclaimer()
Definition Utility.cpp:49
static bool DisclaimerPrinted
Definition Utility.h:28
Implements the possibility of a generalized calculation of the densities. For example,...
Abstract base class for an HRG model implementation.
virtual void CalculateSusceptibilityMatrix()
Calculates the conserved charges susceptibility matrix.
virtual double FullIdealChemicalPotential(int i) const
Chemical potential entering the ideal gas expressions of particle species i.
virtual void FixParametersNoReset()
Method which actually implements ConstrainChemicalPotentialsNoReset() (for backward compatibility).
virtual void ChangeTPS(ThermalParticleSystem *TPS)
Change the particle list.
virtual void SetGammaC(double gammaC)
Set the charm quark fugacity factor.
virtual void CalculateParticleChargeCorrelationMatrix()
Calculates the matrix of correlators between primordial (and also final) particle numbers and conserv...
virtual void SetTemperature(double T)
Set the temperature.
virtual void CalculateFluctuations()
Computes the fluctuation observables.
bool UsePartialChemicalEquilibrium()
Whether partial chemical equilibrium with additional chemical potentials is used.
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,...
virtual double TwoParticleSusceptibilityPrimordialByPdg(long long id1, long long id2)
Returns the computed primordial particle number (cross-)susceptibility for particles with pdg codes ...
virtual std::vector< double > CalculateChargeFluctuations(const std::vector< double > &chgs, int order=4, bool dimensionfull=false)
Calculates fluctuations (diagonal susceptibilities) of an arbitrary "conserved" charge.
virtual void SetVirial(int, int, double)
Set the excluded volume coefficient .
virtual void FillChemicalPotentials()
Sets the chemical potentials of all particles.
virtual void CalculateTemperatureDerivatives()
Computes the temperature derivatives of densities, shifted chemical potentials, and primordial hadron...
void ConstrainChemicalPotentials(bool resetInitialValues=true)
Constrains the chemical potentials by the conservation laws imposed.
virtual void FixParameters()
Method which actually implements ConstrainChemicalPotentials() (for backward compatibility).
virtual double TwoParticleSusceptibilityFinalByPdg(long long id1, long long id2)
Returns the computed final particle number (cross-)susceptibility for particles with pdg codes id1 a...
virtual double TwoParticleSusceptibilityFinal(int i, int j) const
Returns the computed final particle number (cross-)susceptibility for particles with ids i and j....
virtual void SetGammaS(double gammaS)
Set the strange quark fugacity factor.
virtual void SetCharm(int C)
Set the total charm (for canonical ensemble only)
virtual double FinalNetParticleChargeSusceptibilityByPdg(long long id1, ConservedCharge::Name chg)
Returns the computed final (after decays) net particle vs conserved charge (cross-)susceptibility fo...
void SetResonanceWidthIntegrationType(ThermalParticle::ResonanceWidthIntegration type)
Set the ThermalParticle::ResonanceWidthIntegration scheme for all particles. Calls the corresponding ...
virtual void CalculateFeeddown()
Calculates the total densities which include feeddown contributions.
virtual double TwoParticleSusceptibilityTemperatureDerivativePrimordial(int i, int j) const
Returns the computed temperature derivative of the primordial particle number (cross-)susceptibility ...
void SetUseWidth(bool useWidth)
Sets whether finite resonance widths are used. Deprecated.
virtual double FinalParticleChargeSusceptibilityByPdg(long long id1, ConservedCharge::Name chg)
Returns the computed final (after decays) particle vs conserved charge (cross-)susceptibility for pa...
virtual void DisableBaryonAntiBaryonAttraction()
virtual double FinalParticleChargeSusceptibility(int i, ConservedCharge::Name chg) const
Returns the computed final (after decays) particle vs conserved charge (cross-)susceptibility for pa...
virtual void SetChemicalPotentials(const std::vector< double > &chem=std::vector< double >(0))
Sets the chemical potentials of all particles.
double ChemicalPotential(int i) const
Chemical potential of particle species i.
virtual void SetElectricCharge(int Q)
Set the total electric charge (for canonical ensemble only)
virtual void SetBaryonChemicalPotential(double muB)
Set the baryon chemical potential.
virtual void CalculateTwoParticleCorrelations()
Computes the fluctuations and correlations of the primordial particle numbers.
virtual double SusceptibilityDimensionfull(ConservedCharge::Name i, ConservedCharge::Name j) const
A 2nd order susceptibility of conserved charges.
void SetNormBratio(bool normBratio)
Whether branching ratios are renormalized to 100%.
virtual void SetParameters(const ThermalModelParameters &params)
The thermal parameters.
virtual void SetElectricChemicalPotential(double muQ)
Set the electric chemical potential.
virtual double PrimordialNetParticleChargeSusceptibilityByPdg(long long id1, ConservedCharge::Name chg)
Returns the computed primordial net particle vs conserved charge (cross-)susceptibility for particle...
virtual double TwoParticleSusceptibilityTemperatureDerivativePrimordialByPdg(long long id1, long long id2)
Returns the computed temperature derivative of the primordial particle number (cross-)susceptibility ...
virtual double NetParticleSusceptibilityFinalByPdg(long long id1, long long id2)
Returns the computed (cross-)susceptibility between final net-particle numbers for pdg codes id1 and...
virtual void SetChemicalPotential(int i, double chem)
Sets the chemical potential of particle species i.
virtual bool FixChemicalPotentialsThroughDensities(double rhoB=0., double rhoQ=0., double rhoS=0., double rhoC=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 baryon,...
virtual void ValidateCalculation()
Checks whether issues have occured during the calculation of particle densities in the CalculateDensi...
int ComponentsNumber() const
Number of different particle species in the list.
virtual void SetBaryonCharge(int B)
Set the total baryon number (for canonical ensemble only)
virtual void CalculateProxySusceptibilityMatrix()
Calculates the susceptibility matrix of conserved charges proxies.
ThermalModelBase(ThermalParticleSystem *TPS, const ThermalModelParameters &params=ThermalModelParameters())
Construct a new ThermalModelBase object.
virtual double PrimordialParticleChargeSusceptibility(int i, ConservedCharge::Name chg) const
Returns the computed primordial particle vs conserved charge (cross-)susceptibility for particle wit...
virtual void CalculatePrimordialDensities()=0
Calculates the primordial densities of all species.
virtual void SetStrangenessChemicalPotential(double muS)
Set the strangeness chemical potential.
virtual double TwoParticleSusceptibilityPrimordial(int i, int j) const
Returns the computed primordial particle number (cross-)susceptibility for particles with ids i and ...
virtual void CalculateDensities()
Calculates the primordial and total (after decays) densities of all species.
virtual void FillVirial(const std::vector< double > &ri=std::vector< double >(0))
Fills the excluded volume coefficients based on the provided radii parameters for all species.
virtual void SetGammaq(double gammaq)
Set the light quark fugacity factor.
virtual void SetStrangeness(int S)
Set the total strangeness (for canonical ensemble only)
virtual void SetAttraction(int, int, double)
Set the vdW mean field attraction coefficient .
virtual double PrimordialParticleChargeSusceptibilityByPdg(long long id1, ConservedCharge::Name chg)
Returns the computed primordial particle vs conserved charge (cross-)susceptibility for particle wit...
const ThermalModelParameters & Parameters() const
virtual double NetParticleSusceptibilityPrimordialByPdg(long long id1, long long id2)
Returns the computed (cross-)susceptibility between primordial net-particle numbers for pdg codes id...
virtual void SetCharmChemicalPotential(double muC)
Set the charm chemical potential.
virtual void ApplyBaryonAnnihilation(double gammaBbar)
[EXPERIMENTAL] Applies a baryon annihilation toy model to baryon yields and susceptibilities with the...
virtual void SetStatistics(bool stats)
double Volume() const
System volume (fm )
@ GCE
Grand canonical ensemble.
virtual void CalculateTwoParticleFluctuationsDecays()
Computes particle number correlations and fluctuations for all final-state particles which are marked...
Class containing all information about a particle specie.
double AbsoluteCharm() const
Absolute charm quark content |s|.
int BaryonCharge() const
Particle's baryon number.
double Density(const ThermalModelParameters &params, IdealGasFunctions::Quantity type=IdealGasFunctions::ParticleDensity, bool useWidth=0, double mu=0.) const
Computes a specified ideal gas thermodynamic function.
const ParticleDecaysVector & Decays() const
A vector of particle's decays.
ResonanceWidthIntegration
Treatment of finite resonance widths.
@ eBW
Energy-dependent Breit-Wigner scheme (eBW)
@ BWTwoGamma
Energy-independent Breit-Wigner in +-2\Gamma interval.
@ ZeroWidth
Zero-width approximation.
double AbsoluteStrangeness() const
Absolute strange quark content |s|.
double AbsoluteQuark() const
Absolute light quark content |u,d|.
const ParticleDecaysVector & DecaysOriginal() const
A backup copy of particle's decays.
Class containing the particle list.
std::vector< std::pair< double, std::vector< int > > > ResonanceFinalStatesDistribution
std::vector< SingleDecayContribution > DecayContributionsToParticle
A vector of SingleDecayContribution where each element corresponds to a certain resonance species.
constexpr double GeVtoifm()
A constant to transform GeV into fm .
Definition xMath.h:25
constexpr double GeVtoifm3()
A constant to transform GeV into fm .
Definition xMath.h:31
constexpr double mnucleon()
Nucleon's mass. Value as in UrQMD.
Definition xMath.h:34
The main namespace where all classes and functions of the Thermal-FIST library reside.
Definition CosmicEoS.h:9
MatrixXd GetSusceptibilityMatrix(ThermalModelBase *model, const vector< int > &ConservedDensities)
MatrixXd GetPressureHessianMatrix(ThermalModelBase *model, const vector< int > &ConservedDensities)
Name
Set of all conserved charges considered.
@ ElectricCharge
Electric charge.
@ Strong
Feeddown from strong decays.
@ Weak
Feeddown from strong, electromagnetic, and weak decays.
@ StabilityFlag
Feeddown from all particles marked as unstable.
@ Primordial
No feeddown.
static const int NumberOfDecayTypes
Structure containing all thermal parameters of the model.