29 if (mean <= 0)
return 0;
31 double expmean = exp(-mean);
37 if (pir <= expmean)
break;
61 return static_cast<int> (em);
74 if (mean <= 0)
return 0;
76 double expmean = exp(-mean);
82 if (pir <= expmean)
break;
98 y = tan(pi*rangen.rand());
104 }
while (rangen.rand() > t);
106 return static_cast<int> (em);
118 return exp(-(mu1 + mu2)) * pow(sqrt(mu1 / mu2), k) *
xMath::BesselI(k, 2. * sqrt(mu1 * mu2));
122 double SiemensRasmussenMomentumGenerator::g(
double x,
double mass)
const {
126 double talpha = alpha(tp);
128 double en = sqrt(tp * tp + mass * mass);
129 double sh = sinh(talpha);
130 double shtalpha = 1.;
132 shtalpha = sh / talpha;
133 double ch = sqrt(1. + sh * sh);
134 return tp * tp * exp(-m_Gamma * en / m_T) * ((1. + m_T / m_Gamma / en)*shtalpha - m_T / m_Gamma / en * ch) / x;
137 double SiemensRasmussenMomentumGenerator::g2(
double x,
double tp,
double mass)
const {
140 double talpha = alpha(tp);
142 double en = sqrt(tp * tp + mass * mass);
143 double sh = sinh(talpha);
144 double shtalpha = 1.;
146 shtalpha = sh / talpha;
147 double ch = sqrt(1. + sh * sh);
148 return tp * tp * exp(-m_Gamma * en / m_T) * ((1. + m_T / m_Gamma / en)*shtalpha - m_T / m_Gamma / en * ch) / x;
151 void SiemensRasmussenMomentumGenerator::FixParameters() {
153 double l = 0., r = 1.;
154 double m1 = l + (r - l) / 3.;
155 double m2 = r - (r - l) / 3.;
158 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
165 m1 = l + (r - l) / 3.;
166 m2 = r - (r - l) / 3.;
169 m_Max = g((m1 + m2) / 2.);
172 double SiemensRasmussenMomentumGenerator::GetRandom(
double mass)
const {
177 double y0 = m_Max *
randgenMT.randDblExc();
181 if (y0 < mn * g(x0))
return -log(x0);
187 std::vector<double> ret(0);
188 double tp = GetRandom(mass);
190 double cthe = 2. *
randgenMT.rand() - 1.;
191 double sthe = sqrt(1. - cthe * cthe);
192 ret.push_back(tp*cos(tphi)*sthe);
193 ret.push_back(tp*sin(tphi)*sthe);
194 ret.push_back(tp*cthe);
204 double ThermalMomentumGenerator::g(
double x,
double mass)
const
209 double texp = exp((sqrt(mass * mass + tp * tp) - m_Mu) / m_T);
210 return tp * tp / (texp + m_Statistics) / x;
236 double ThermalMomentumGenerator::ComputeMaximum(
double mass)
const
239 double l = 0., r = 1.;
240 double m1 = l + (r - l) / 3.;
241 double m2 = r - (r - l) / 3.;
244 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
245 if (g(m1, mass) < g(m2, mass)) {
251 m1 = l + (r - l) / 3.;
252 m2 = r - (r - l) / 3.;
255 return g((m1 + m2) / 2., mass);
265 if (mass < m_Mu && m_Statistics == -1)
266 std::cerr <<
"**WARNING** ThermalMomentumGenerator::GetP: Bose-condensation mu " << m_Mu <<
" > mass " << mass << std::endl;
270 M = ComputeMaximum(mass);
272 double prob = g(x0, mass) / M;
275 std::cerr <<
"**WARNING** ThermalMomentumGenerator::GetP: Probability exceeds unity by " << prob - 1. << std::endl;
277 if (
randgenMT.randDblExc() < prob)
return -log(x0);
284 double Tkin,
double etamax,
double mass,
int statistics,
double mu) :
285 m_FreezeoutModel(FreezeoutModel),
286 m_Tkin(Tkin), m_EtaMax(etamax), m_Mass(mass),
287 m_Generator(mass, statistics, Tkin, mu)
289 if (m_FreezeoutModel == NULL) {
296 if (m_FreezeoutModel != NULL)
297 delete m_FreezeoutModel;
310 double betar = m_FreezeoutModel->tanhetaperp(zetacand);
311 double cosheta = cosh(eta);
312 double sinheta = sinh(eta);
314 double cosphi = cos(ph);
315 double sinphi = sin(ph);
317 double vx = betar * cosphi / cosheta;
318 double vy = betar * sinphi / cosheta;
319 double vz = tanh(eta);
321 double dRdZeta = m_FreezeoutModel->dRdZeta(zetacand);
322 double dtaudZeta = m_FreezeoutModel->dtaudZeta(zetacand);
324 double coshetaperp = m_FreezeoutModel->coshetaperp(zetacand);
325 double sinhetaperp = m_FreezeoutModel->sinhetaperp(zetacand);
327 std::vector<double> dsigma_lab;
328 dsigma_lab.push_back(dRdZeta * cosheta);
329 dsigma_lab.push_back(dtaudZeta * cosphi);
330 dsigma_lab.push_back(dtaudZeta * sinphi);
331 dsigma_lab.push_back(dRdZeta * sinheta);
334 std::vector<double> dsigma_loc =
LorentzBoost(dsigma_lab, vx, vy, vz);
337 double maxWeight = 1. + std::abs(dsigma_loc[1] / dsigma_loc[0]) + std::abs(dsigma_loc[2] / dsigma_loc[0]) + std::abs(dsigma_loc[3] / dsigma_loc[0]);
344 double tp = m_Generator.GetP(mass);
347 double sthe = sqrt(1. - cthe * cthe);
348 part.
px = tp * cos(tphi) * sthe;
349 part.
py = tp * sin(tphi) * sthe;
351 part.
p0 = sqrt(mass * mass + tp * tp);
353 double p0LRF = part.
p0;
355 double dsigmamu_pmu_loc = dsigma_loc[0] * part.
p0
356 - dsigma_loc[1] * part.
px - dsigma_loc[2] * part.
py - dsigma_loc[3] * part.
pz;
359 double dsigmamu_umu_loc = dsigma_loc[0];
361 double dumu_pmu_loc = p0LRF;
363 double Weight = dsigmamu_pmu_loc / dsigmamu_umu_loc / dumu_pmu_loc / maxWeight;
366 std::cerr <<
"**WARNING** BoostInvariantHypersurfaceMomentumGenerator::GetMomentum: Weight exceeds unity by " << Weight - 1. << std::endl;
374 if (betar != 0.0 || eta != 0.0)
378 std::vector<double> ret(3, 0.);
384 double tau = m_FreezeoutModel->taufunc(zetacand);
385 double r0 = tau * cosheta;
386 double rz = tau * sinheta;
388 double Rperp = m_FreezeoutModel->Rfunc(zetacand);
389 double rx = Rperp * cosphi;
390 double ry = Rperp * sinphi;
401 if (m_FreezeoutModel->InverseZetaDistributionIsExplicit())
402 return m_FreezeoutModel->InverseZetaDistribution(rangen.rand());
405 double zetacand = rangen.rand();
407 double prob = m_FreezeoutModel->ZetaProbability(zetacand) / m_FreezeoutModel->ProbabilityMaximum();
409 if (rangen.rand() < prob)
416 double BreitWignerGenerator::f(
double x)
const {
417 return x / ((x*x - m_M * m_M)*(x*x - m_M * m_M) + m_M * m_M*m_Gamma*m_Gamma);
420 void BreitWignerGenerator::FixParameters() {
422 double l = m_Mthr, r = m_M + 2.*m_Gamma;
423 double m1 = l + (r - l) / 3.;
424 double m2 = r - (r - l) / 3.;
427 while (fabs(m2 - m1) < eps && iter < MAXITERS) {
434 m1 = l + (r - l) / 3.;
435 m2 = r - (r - l) / 3.;
438 m_Max = f((m1 + m2) / 2.);
443 if (m_Gamma < 1e-7)
return m_M;
445 double x0 = m_Mthr + (m_M + 2.*m_Gamma - m_Mthr) *
randgenMT.rand();
447 if (y0 < f(x0))
return x0;
469 double Threshold =
m_part->DecayThresholdMass();
470 double Width =
m_part->ResonanceWidth();
471 double Mass =
m_part->Mass();
473 double a = std::max(Threshold, Mass - 2.*Width);
474 double b = Mass + 2.*Width;
483 for (
int i = 0; i < iters; ++i) {
484 double tM =
m_Xmin + dM * i;
502 if (y0 <
f(x0))
return x0;
509 double Threshold =
m_part->DecayThresholdMass();
510 double Width =
m_part->ResonanceWidth();
511 double Mass =
m_part->Mass();
513 double a = std::max(Threshold, Mass - 2.*Width);
514 double b = Mass + 2.*Width;
516 if (
m_part->Decays().size() == 0)
517 a = Mass - 2.*Width + 1.e-6;
519 a =
m_part->DecayThresholdMassDynamical();
524 m_Xmax = b + 0.2 * (b - a);
533 for (
int i = 0; i < iters; ++i) {
534 double tM =
m_Xmin + dM * i;
561 if (nu < 0 || n < 0)
return 0.0;
563 double logret = (2. * n + nu) * log(a/2.) - a;
564 for (
int i = 1; i <= n; ++i)
565 logret -= 2. * log(i);
566 for (
int i = n + 1; i <= n + nu; ++i)
574 double hn2 = 1., hn1 = 0., hn = 0.;
575 double kn2 = 0., kn1 = 1., kn = 0.;
577 for (
int n = 1; n <= nmax; ++n) {
578 double an = 2. * (nu + n) / x;
588 if (n == nmax && nmax <= 1000 && abs(hn2 / kn2 - hn1 / kn1) > 1.e-9)
592 std::cerr <<
"**WARNING** BesselDistributionGenerator::R(x,nu): Reached maximum iterations..." << std::endl;
600 return mu(a, nu) + (1. / 4.) * a * a *
R(a, nu) * (
R(a,nu+1) -
R(a,nu));
605 double tmp =
m(a, nu) -
mu(a, nu);
606 return chi2(a, nu) + tmp * tmp;
611 double A = sqrt(a*a + nu*nu);
612 double B = sqrt(a*a + (nu+1.)*(nu+1.));
613 double tmp = 1. + a * a * (1. + B - A) / 2. / (nu + A) / (nu + 1. + B);
614 return a*a / 2. / (nu + A) + tmp * tmp;
619 if (nu < 0 || a <= 0.)
return 0;
654 for (
int i = 1; i <= X; ++i) {
655 ret *= (a / 2.)/(tm + i);
656 ret *= (a / 2.)/(tm + nu + i);
660 for (
int i = 1; i <= -X; ++i) {
661 ret *= (tm + X + i) / (a / 2.);
662 ret *= (tm + nu + X + i) / (a / 2.);
672 double pm =
pn(tm, a, nu);
673 double w = 1. + pm / 2.;
675 double U = rangen.rand();
676 double W = rangen.rand();
678 if (rangen.rand() < 0.5)
682 if (U <= w / (1. + w)) {
683 double V = rangen.rand();
687 double E = -log(rangen.randDblExc());
690 #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
691 int X = S * std::lround(Y);
693 int X = S * ((Y > 0.0) ? std::floor(Y + 0.5) : std::ceil(Y - 0.5));
701 if (pratio != pratio) {
702 std::cerr <<
"**WARNING** BesselDistributionGenerator::RandomBesselDevroye1: Float problem!" << std::endl;
706 if (W * std::min(1., exp(w - pm * Y)) <= pratio)
715 double tsig = sqrt(
sig2(a, nu));
716 double q = std::min(1. / tsig / sqrt(648.), 1. / 3.);
718 double U = rangen.rand();
719 double W = rangen.rand();
721 if (rangen.rand() < 0.5)
725 if (U <= (1. + 2. / q) / (1. + 4. / q)) {
726 double V = rangen.rand();
727 Y = V * (1. / 2. + 1. / q);
730 double E = -log(rangen.randDblExc());
731 Y = 1. / 2. + 1. / q + E / q;
733 #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
734 int X = S * std::lround(Y);
736 int X = S * ((Y > 0.0) ? std::floor(Y + 0.5) : std::ceil(Y - 0.5));
744 if (pratio != pratio) {
745 std::cerr <<
"**WARNING** BesselDistributionGenerator::RandomBesselDevroye2: Float problem!" << std::endl;
749 if (W * std::min(1., exp(1. + q/2. - q*Y)) <= pratio)
758 double tQ = sqrt(
Q2(a, nu));
759 double q = std::min(1. / tQ / sqrt(648.), 1. / 3.);
761 double U = rangen.rand();
762 double W = rangen.rand();
764 if (rangen.rand() < 0.5)
768 if (U <= (1. + 2. / q) / (1. + 4. / q)) {
769 double V = rangen.rand();
770 Y = V * (1. / 2. + 1. / q);
773 double E = -log(rangen.randDblExc());
774 Y = 1. / 2. + 1. / q + E / q;
776 #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
777 int X = S * std::lround(Y);
779 int X = S * ((Y > 0.0) ? std::floor(Y + 0.5) : std::ceil(Y - 0.5));
787 if (pratio != pratio) {
788 std::cerr <<
"**WARNING** BesselDistributionGenerator::RandomBesselDevroye3: Float problem!" << std::endl;
793 if (W * std::min(1., exp(1. + q/2. - q*Y)) <= pratio)
802 while (ret <= -0.5) {
803 ret = rangen.randNorm(
mu(a,nu), sqrt(
chi2(a,nu)));
805 return static_cast<int>(ret + 0.5);
828 double sinth = sqrt(1. - costh * costh);
830 double vx =
GetBeta() * sinth * cos(ph);
831 double vy =
GetBeta() * sinth * sin(ph);
836 double tp = m_Generator.GetP(mass);
839 double sthe = sqrt(1. - cthe * cthe);
840 part.
px = tp * cos(tphi) * sthe;
841 part.
py = tp * sin(tphi) * sthe;
843 part.
p0 = sqrt(mass * mass + tp * tp);
848 std::vector<double> ret(7);
855 ret[4] =
GetR() * sinth * cos(ph);
856 ret[5] =
GetR() * sinth * sin(ph);
857 ret[6] =
GetR() * costh;
872 namespace RandomGenerators {
874 void SSHMomentumGenerator::FixParameters() {
877 double l = 0., r = 1.;
878 double m1 = l + (r - l) / 3.;
879 double m2 = r - (r - l) / 3.;
882 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
889 m1 = l + (r - l) / 3.;
890 m2 = r - (r - l) / 3.;
893 m_MaxPt = g((m1 + m2) / 2.);
897 for (
double x = 0.5 * dx; x <= 1.; x += dx) {
898 m_dndpt.add_val(x, g(x));
906 for (
double x = 0.5 * dx; x <= 1.; x += dx) {
911 double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
912 double m1 = l + (r - l) / 3.;
913 double m2 = r - (r - l) / 3.;
916 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
917 if (m_distr.dndy(m1, pt) < m_distr.dndy(m2, pt)) {
923 m1 = l + (r - l) / 3.;
924 m2 = r - (r - l) / 3.;
927 m_MaxYs.push_back(m_distr.dndy((m1 + m2) / 2., pt));
929 m_dndy.push_back(SplineFunction());
931 for (
double ty = -4. - m_EtaMax + 0.5 * dy; ty <= 4. + m_EtaMax; ty += dy) {
932 m_dndy[m_dndy.size() - 1].add_val(ty, m_distr.dndy(ty, pt));
938 void SSHMomentumGenerator::FixParameters2() {
941 double l = 0., r = 1.;
942 double m1 = l + (r - l) / 3.;
943 double m2 = r - (r - l) / 3.;
946 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
953 m1 = l + (r - l) / 3.;
954 m2 = r - (r - l) / 3.;
957 m_MaxPt = g((m1 + m2) / 2.);
961 for (
double x = 0.5 * dx; x <= 1.; x += dx) {
962 m_dndpt.add_val(x, g(x));
970 for (
double x = 0.5 * dx; x <= 1.; x += dx) {
975 double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
976 double m1 = l + (r - l) / 3.;
977 double m2 = r - (r - l) / 3.;
980 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
981 if (m_distr.dndysingle(m1, pt) < m_distr.dndysingle(m2, pt)) {
987 m1 = l + (r - l) / 3.;
988 m2 = r - (r - l) / 3.;
991 m_MaxYs.push_back(m_distr.dndysingle((m1 + m2) / 2., pt));
993 m_dndy.push_back(SplineFunction());
995 for (
double ty = -4. + 0.5 * dy; ty <= 4.; ty += dy) {
996 m_dndy[m_dndy.size() - 1].add_val(ty, m_distr.dndysingle(ty, pt));
1003 void SSHMomentumGenerator::FindMaximumPt() {
1006 double l = 0., r = 1.;
1007 double m1 = l + (r - l) / 3.;
1008 double m2 = r - (r - l) / 3.;
1011 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
1012 if (g(m1) < g(m2)) {
1018 m1 = l + (r - l) / 3.;
1019 m2 = r - (r - l) / 3.;
1022 m_MaxPt = g((m1 + m2) / 2.);
1026 for (
double x = 0.5 * dx; x <= 1.; x += dx) {
1027 m_dndpt.add_val(x, g(x));
1031 void SSHMomentumGenerator::FindMaximumY(
double pt) {
1033 double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
1034 double m1 = l + (r - l) / 3.;
1035 double m2 = r - (r - l) / 3.;
1038 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
1039 if (m_distr.dndy(m1, pt) < m_distr.dndy(m2, pt)) {
1045 m1 = l + (r - l) / 3.;
1046 m2 = r - (r - l) / 3.;
1049 m_MaxY = m_distr.dndy((m1 + m2) / 2., pt);
1052 void SSHMomentumGenerator::FindMaximumY2(
double pt) {
1054 double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
1055 double m1 = l + (r - l) / 3.;
1056 double m2 = r - (r - l) / 3.;
1059 while (fabs(m2 - m1) > eps && iter < MAXITERS) {
1060 if (m_distr.dndysingle(m1, pt) < m_distr.dndysingle(m2, pt)) {
1066 m1 = l + (r - l) / 3.;
1067 m2 = r - (r - l) / 3.;
1070 m_MaxY = m_distr.dndysingle((m1 + m2) / 2., pt);
1073 std::pair<double, double> SSHMomentumGenerator::GetRandom(
double mass) {
1074 double tpt = 0., ty = 0.;
1077 double y0 = m_MaxPt *
randgenMT.randDblExc();
1084 int ind = (int)(exp(-tpt) / m_dPt);
1085 if (ind < 0) ind = 0;
1086 if (ind >=
static_cast<int>(m_dndy.size())) ind = m_dndy.size() - 1;
1087 double x0 = -4. - m_EtaMax + (8. + 2. * m_EtaMax) *
randgenMT.randDblExc();
1088 double y0 = m_MaxYs[ind] *
randgenMT.randDblExc();
1089 if (y0 < m_dndy[ind].f(x0)) {
1094 return std::make_pair(tpt, ty);
1097 std::pair<double, double> SSHMomentumGenerator::GetRandom2(
double mass)
const {
1098 double tpt = 0., ty = 0., teta = 0.;
1101 double y0 = m_MaxPt *
randgenMT.randDblExc();
1108 int ind = (int)(exp(-tpt) / m_dPt);
1109 if (ind < 0) ind = 0;
1110 if (ind >=
static_cast<int>(m_dndy.size())) ind = m_dndy.size() - 1;
1111 double x0 = -4. + (8.) *
randgenMT.randDblExc();
1112 double y0 = m_MaxYs[ind] *
randgenMT.randDblExc();
1114 if (y0 < m_dndy[ind].f(x0)) {
1116 teta = -m_EtaMax + 2. * m_EtaMax *
randgenMT.randDblExc();
1120 return std::make_pair(tpt, ty - teta);
1124 std::vector<double> ret(0);
1125 std::pair<double, double> pty = GetRandom2(mass);
1126 double tpt = pty.first;
1127 double ty = pty.second;
1129 ret.push_back(tpt * cos(tphi));
1130 ret.push_back(tpt * sin(tphi));
1131 ret.push_back(sqrt(tpt * tpt + m_Mass * m_Mass) * sinh(ty));
Base class implementing a longitudinally boost-invariant azimuthally symmetric freeze-out parametriza...
virtual std::vector< double > GetMomentum(double mass=-1.) const
BoostInvariantMomentumGenerator(BoostInvariantFreezeoutParametrization *FreezeoutModel=NULL, double Tkin=0.100, double etamax=3.0, double mass=0.938, int statistics=0, double mu=0)
Construct a new BoostInvariantMomentumGenerator object.
virtual double GetRandomZeta(MTRand &rangen=RandomGenerators::randgenMT) const
Samples zeta for use in Monte Carlo event generator.
virtual ~BoostInvariantMomentumGenerator()
BoostInvariantMomentumGenerator desctructor.
void SetParameters(double M, double gamma, double mthr)
Set the Breit-Wigner spectral function parameters.
double GetRandom() const
Samples the resonance mass from a relativistic Breit-Wigner distribution with a constant width.
std::vector< double > GetMomentum(double mass=-1.) const
virtual std::vector< double > GetMomentum(double mass=-1.) const
virtual std::vector< double > GetMomentum(double mass=-1.) const
virtual void FixParameters()
Computes some auxiliary stuff needed for sampling.
void SetParameters(ThermalParticle *part, double T, double Mu)
Sets the parameters of the distribution.
double GetRandom() const
Samples the mass.
virtual double f(double M) const
Unnormalized resonance mass probability density.
virtual double f(double M) const
Unnormalized resonance mass probability density.
virtual void FixParameters()
Computes some auxiliary stuff needed for sampling.
double GetP(double mass=-1.) const
Samples the momentum of a particle.
Class containing all information about a particle specie.
SimpleParticle LorentzBoostMomentumOnly(const SimpleParticle &part, double vx, double vy, double vz)
Lorentz boost of the 4-momentum of a particle.
Contains random generator functions used in the Monte Carlo Thermal Event Generator.
double SkellamProbability(int k, double mu1, double mu2)
Probability of a Skellam distributed random variable with Poisson means mu1 and mu2 to have the value...
int RandomPoisson(double mean)
Generates random integer distributed by Poisson with specified mean Uses randgenMT.
MTRand randgenMT
The Mersenne Twister random number generator.
void SetSeed(const unsigned int seed)
Set the seed of the random number generator randgenMT.
std::mt19937 rng_std
The Mersenne Twister random number generator from STD library.
constexpr double Pi()
Pi constant.
double BesselIexp(int n, double x)
Modified Bessel function I_n(x), divided by exponential factor.
double LogGamma(double x)
Computes the logarithm of the Gamma function.
double BesselI(int n, double x)
Integer order modified Bessel function I_n(x)
The main namespace where all classes and functions of the Thermal-FIST library reside.
std::vector< double > LorentzBoost(const std::vector< double > &fourvector, double vx, double vy, double vz)
Performs a Lorentz boost on a four-vector.
static double chi2(double a, int nu)
static int RandomBesselNormal(double a, int nu, MTRand &rangen)
static int RandomBesselDevroye1(double a, int nu, MTRand &rangen)
static int RandomBesselDevroye3(double a, int nu, MTRand &rangen)
static double pmXmOverpm(int X, int tm, double a, int nu)
static double mu(double a, int nu)
static double Q2(double a, int nu)
static int RandomBesselCombined(double a, int nu, MTRand &rangen)
static int RandomBesselPoisson(double a, int nu, MTRand &rangen)
static int m(double a, int nu)
static int RandomBesselDevroye2(double a, int nu, MTRand &rangen)
static double pn(int n, double a, int nu)
static double R(double x, int nu)
static double sig2(double a, int nu)
Structure holding information about a single particle in the event generator.
double pz
3-momentum components (in GeV)
Contains some extra mathematical functions used in the code.