#include "machdefs.h" #include #include #include #include #include #include #include "pexceptions.h" #include "constcosmo.h" #include "geneutils.h" #include "pkspectrum.h" namespace SOPHYA { /////////////////////////////////////////////////////////// //******************** InitialSpectrum ******************// /////////////////////////////////////////////////////////// InitialSpectrum::InitialSpectrum(double n,double a) : n_(n), A_(a) { } InitialSpectrum::InitialSpectrum(InitialSpectrum& pkinf) : n_(pkinf.n_), A_(pkinf.A_) { } InitialSpectrum::~InitialSpectrum(void) { } /////////////////////////////////////////////////////////// //****************** TransfertEisenstein ****************// /////////////////////////////////////////////////////////// // From Eisenstein & Hu ApJ 496:605-614 1998 April 1 (ou astro-ph/9709112) TransfertEisenstein::TransfertEisenstein(double h100,double OmegaCDM0,double OmegaBaryon0,double tcmb,bool nobaryon,int lp) : lp_(lp) , Oc_(OmegaCDM0) , Ob_(OmegaBaryon0) , h100_(h100) , tcmb_(tcmb) , nobaryon_(nobaryon) , nooscenv_(0), retpart_(ALL) { zero_(); Init_(); } TransfertEisenstein::TransfertEisenstein(TransfertEisenstein& tf) : lp_(tf.lp_) ,Oc_(tf.Oc_) , Ob_(tf.Ob_) , h100_(tf.h100_) , tcmb_(tf.tcmb_) , nobaryon_(tf.nobaryon_) , nooscenv_(tf.nooscenv_), retpart_(tf.retpart_) { zero_(); Init_(); } TransfertEisenstein::~TransfertEisenstein(void) { } void TransfertEisenstein::zero_(void) { th2p7_=zeq_=keq_=zd_=Req_=Rd_=s_=ksilk_=alphac_=betac_=bnode_ =alphab_=betab_=alphag_=sfit_=kpeak_=1.e99; } void TransfertEisenstein::Init_(void) { O0_ = Oc_ + Ob_; if(nobaryon_) {O0_ = Oc_; Ob_ = 0.;} double H0 = 100. * h100_, h2 = h100_*h100_; if(lp_) cout<<"h100="< H0/C si on utilise la premiere formule) // keq_ = sqrt(2.*O0_*H0*H0*zeq_) / SpeedOfLight_Cst; keq_ = 7.46e-2 * O0_ * h2 / (th2p7_*th2p7_); if(lp_) cout<<"keq = "< (1+zd) Rd_ = 31.5*Ob_*h2 / th2p7P4 * (1.e3/zd_); //in tf_fit.c: Rd_ = 31.5*Ob_*h2 / th2p7P4 * (1.e3/(1.+zd_)); if(lp_) { cout<<"Req = "< zeq double y = (1.+zeq_)/(1.+zd_); //in tf_fit.c: double y = zeq_/(1.+zd_); double s1py = sqrt(1.+y); double Gy = y*( -6.*s1py + (2.+3.*y)*log((s1py+1.)/(s1py-1.)) ); alphab_ = 2.07*keq_*s_*pow(1.+Rd_,-3./4.)*Gy; // Formule 24 page 610 betab_ = 0.5 + Ob_/O0_ + (3.-2.*Ob_/O0_) * sqrt(pow(17.2*O0_*h2,2.) + 1.); if(lp_) cout<<"alphab = "<0.) {h100_ = h100; haschanged = true;} if(OmegaCDM0>0.) {Oc_ = OmegaCDM0; haschanged = true;} if(OmegaBaryon0>0.) {Ob_ = OmegaBaryon0; haschanged = true;} // et on recalcule les initialisations if(haschanged) Init_(); return haschanged; } void TransfertEisenstein::SetNoOscEnv(unsigned short nooscenv) // To obtain an approximate form of the non-oscillatory part of the transfert function // nooscenv = 0 : use the baryon oscillatory part of transfert function (full tf) // nooscenv = 1 : use approx. paragraph 3.3 p610 (middle of right column) // Replace j0(k*stilde) -> [1+(k*stilde)^4]^(-1/4) // nooscenv = 2 : use formulae 29+30+31 page 612 // The value of an approximate transfer function that captures // the non-oscillatory part of a partial baryon transfer function. // In other words, the baryon oscillations are left out, // but the suppression of power below the sound horizon is included. { if(nooscenv!=1 && nooscenv!=2) nooscenv = 0; nooscenv_ = nooscenv; } void TransfertEisenstein::SetReturnPart(ReturnPart retpart) // To return only baryon or CDM part part of transfert function // retpart = ALL: return full transfert function // = CDM : return only CDM part of transfert function // = BARYON : return only Baryon part of transfert function // WARNING: only relevant for nobaryon_=false AND nooscenv!=2 { retpart_ = retpart; } void TransfertEisenstein::SetPrintLevel(int lp) { lp_ = lp; } double TransfertEisenstein::T0tild(double k,double alphac,double betac) { // Formule 10 p 608 //double q = k*th2p7_*th2p7_/(O0_*h100_*h100_); double q = k/(13.41*keq_); // Formule 20 p 610 double C = (14.2/alphac) + 386./(1.+69.9*pow(q,1.08)); // Formule 19 p 610 double x = log(M_E+1.8*betac*q); return x / (x + C*q*q); } double TransfertEisenstein::operator() (double k) { // --- Pour zero baryon // OU Pour function lissee sans oscillation baryon if(nobaryon_ || nooscenv_ == 2) { double gamma = O0_*h100_; // Calcul de Gamma_eff, formule 30 page 612 (pour fct lissee) if( nobaryon_==false && nooscenv_ == 2 ) gamma = O0_*h100_*(alphag_ + (1.-alphag_)/(1.+pow(0.43*k*sfit_,4.))); // Gamma_eff // Formule 28 page 612 : qui est est equivalent a: // q = k / h100_ * th2p7_*th2p7_ / gamma; // qui est est equivalent a: // q = k / (13.41 * keq) pour Ob=0 // q = k / (13.41 * keq) * (O0*h/Gamma) pour le spectre lisse // Les resultats sont legerement differents a cause des valeurs approx. // des constantes numeriques: on prend comme W.Hu (tf_fit.c) //double q = k / h100_ * th2p7_*th2p7_ / gamma; // Mpc^-1 double q = k/(13.41*keq_) * (O0_*h100_/gamma); // Mpc^-1 // Formules 29 page 612 double l0 = log(2.*M_E + 1.8*q); double c0 = 14.2 + 731./(1.+62.5*q); return l0 / (l0 + c0*q*q); } // --- Pour CDM + Baryons // --- CDM double f = 1. / (1. + pow(k*s_/5.4,4.)); double Tc = f*T0tild(k,1.,betac_) + (1.-f)*T0tild(k,alphac_,betac_); if(retpart_ == CDM) return Tc; // --- Baryons // Formule 22 page 610 double stilde, ksbnode = k*s_/bnode_; if(ksbnode<0.001) stilde =s_ * ksbnode; else stilde = s_ / pow(1. + pow(1./ksbnode,3.), 1./3.); // Formule 21 page 610 double j0kst = 0.; if(nooscenv_ == 1) { j0kst = pow(1.+pow(k*stilde,4.) , -1./4.); //lissee sans oscillation baryon } else { double x = k*stilde; if(x<0.01) j0kst = 1. - x*x/6.*(1.-x*x/20.); else j0kst = sin(x)/x; //cout<<"DEBUG: k="< Mpc tf = (Oc_*tc+Ob_*tb)/(Oc_+Ob_); if(tf>tmax) tmax = tf; k_.push_back(k); tf_.push_back(tf); nread++; } cout<<"TransfertTabulate::ReadCMBFast: nread="< growth=1.) double D1z0 = pow(O0_,4./7.) - Ol_ + (1.+O0_/2.)*(1.+Ol_/70.); D1z0 = 2.5*O0_ / D1z0; // Calcul du growthfactor pour z double Ok = 1. - O0_ - Ol_; double den = Ol_ + Ok*z2 + O0_*z3; double o0z = O0_ *z3 / den; double olz = Ol_ / den; double D1z = pow(o0z,4./7.) - olz + (1.+o0z/2.)*(1.+olz/70.); D1z = 2.5*o0z / z / D1z; return D1z / D1z0; } void GrowthFactor::SetParTo(double OmegaMatter0,double OmegaLambda0) { if(OmegaMatter0>0.) O0_ = OmegaMatter0; Ol_ = OmegaLambda0; } bool GrowthFactor::SetParTo(double OmegaMatter0) // idem precedent sans changer OmegaLambda0 { if(OmegaMatter0<=0.) return false; O0_ = OmegaMatter0; return true; } /////////////////////////////////////////////////////////// //************** PkSpectrum0 et PkSpectrumZ *************// /////////////////////////////////////////////////////////// PkSpectrum0::PkSpectrum0(InitialSpectrum& pkinf,TransfertEisenstein& tf) : pkinf_(pkinf) , tf_(tf) { } PkSpectrum0::PkSpectrum0(PkSpectrum0& pk0) : pkinf_(pk0.pkinf_) , tf_(pk0.tf_) { } PkSpectrum0::~PkSpectrum0(void) { } double PkSpectrum0::operator() (double k) { double tf = tf_(k); double pkinf = pkinf_(k); return pkinf *tf*tf; } //------------------------------------ PkSpectrumZ::PkSpectrumZ(PkSpectrum0& pk0,GrowthFactor& d1,double zref) : pk0_(pk0) , d1_(d1) , zref_(zref) , scale_(1.) , typspec_(PK) { } PkSpectrumZ::PkSpectrumZ(PkSpectrumZ& pkz) : pk0_(pkz.pk0_) , d1_(pkz.d1_) , zref_(pkz.zref_) , scale_(pkz.scale_) , typspec_(PK) { } PkSpectrumZ::~PkSpectrumZ(void) { } void PkSpectrumZ::SetTypSpec(ReturnSpectrum typspec) // typsec = PK : compute Pk(k) // = DELTA : compute Delta^2(k) = k^3*Pk(k)/2Pi^2 { typspec_ = typspec; } double PkSpectrumZ::operator() (double k) { return (*this)(k,zref_); } double PkSpectrumZ::operator() (double k,double z) { double d1 = d1_(z); double v = pk0_(k) * d1*d1; if(typspec_==DELTA) v *= k*k*k/(2.*M_PI*M_PI); return scale_ * v; } /////////////////////////////////////////////////////////// //******************* VarianceSpectrum ******************// /////////////////////////////////////////////////////////// VarianceSpectrum::VarianceSpectrum(GenericFunc& pk,double R,TypeFilter typfilter) : pk_(pk) { SetRadius(R); SetFilter(typfilter); } VarianceSpectrum::VarianceSpectrum(VarianceSpectrum& vpk) : pk_(vpk.pk_) , R_(vpk.R_) { SetFilter(vpk.typfilter_); } VarianceSpectrum::~VarianceSpectrum(void) { } void VarianceSpectrum::SetRadius(double R) // R = taille du filter top-hat ou gaussien { if(R<=0.) { cout<<"VarianceSpectrum::SetRadius: Error R<=0"<0 a l'ordre O(x^6) // DL(x) = 1-x^2*(1-x^2/2) // pour x<0.01 |DL(x)-G(X)^2|<2.0e-13 if(typfilter_ == GAUSSIAN) if(x<0.01) return 1.-x2*(1.-x2/2.); else return exp(-x2); // Filtre top-hat T(x) = 3*(sin(x)-x*cos(x))/x^3 // --- Gestion de la pseudo-divergence pour x->0 // on prend le DL de T(x)^2 pour x->0 a l'ordre O(x^7) // DL(x) = 1-x^2/5*(1-3*x^2/35*(1-4*x^2/81)) // pour x<0.1 |DL(x)-T(X)^2|<2.5e-13 double f2=0.; if(x<0.1) { f2 = 1.-x2/5.*(1.-3.*x2/35.*(1.-4.*x2/81.)); } else { f2 = 3.*(sin(x)-x*cos(x))/(x2*x); f2 *= f2; } return f2; } double VarianceSpectrum::Variance(double kmin,double kmax) // Compute variance of spectrum pk_ by integration // Input: // kmin,kmax = bornes en k de l'integrale pour calculer la variance // Return: // valeur de la variance (sigma^2) // Remarque: // la variance renvoyee est la variance de la masse { if(kmin<=0 || kmax<=0. || kmin>=kmax) { cout<<"VarianceSpectrum::Variance: Error kmin<=0 or kmax<=0 or kmin>=kmax"<=kmax"); } double lkmin = log10(kmin), lkmax = log10(kmax); double var = IntegrateFuncLog(*this,lkmin,lkmax,dperc_,dlogkinc_,dlogkmax_,glorder_); return var; } //------------------------------------ double VarianceSpectrum::FindMaximum(double kmin,double kmax,double eps) // Retourne le maximum de la fonction a integrer // La recherche a lieu entre [kmin,kmax] par pas logarithmiques // Input: // kmin,kmax : intervalle de recherche // eps : precision requise sur les valeurs // Return: // position (en k) du maximum { if(kmin<=0 || kmax<=0. || kmin>=kmax) { cout<<"VarianceSpectrum::FindMaximum: Error kmin<=0 or kmax<=0 or kmin>=kmax || eps<=0"<=kmax || eps<=0"); } int n = 10; // toujours >2 double lkmin = log10(kmin), lkmax = log10(kmax), dlk = (lkmax-lkmin)/n; double lkfind=lkmin, pkfind=-1.; while(1) { for(int i=0; i<=n; i++) { double lk = lkmin + i*dlk; double v = (*this)(pow(10.,lk)); if(vlkmin) lkmin = lkfind-dlk; if(lkfind+dlk= high" (bit0 =1) // rc |= 2 "f(kmax) >= high" (bit1 =1) // rc |= 4 "f(k) < high pour tout k" (bit2 =1) { if(kmin<=0 || kmax<=0. || kmin>=kmax || eps<=0.) { cout<<"VarianceSpectrum::FindLimits: Error kmin<=0 or kmax<=0 or kmin>=kmax or eps<=0"<=kmax || eps<=0"); } int n = 10; // toujours >2 int rc = 0; double lkmin,lkmax,dlk,lkfind; // --- Find kmin lkmin=log10(kmin); lkmax=log10(kmax); dlk=(lkmax-lkmin)/n; while(1) { lkfind = lkmin; for(int i=0;i<=n;i++) { if( (*this)(pow(10,lkfind)) >= high ) break; lkfind = lkmin + i*dlk; } //cout<<"VarianceSpectrum::FindLimits[kmin]: lkfind="<