#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) { } void InitialSpectrum::SetNorm(double a) { A_ = a; } void InitialSpectrum::SetSlope(double n) { n_ = n; } /////////////////////////////////////////////////////////// //****************** 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) , h_(h100) , tcmb_(tcmb) , nobaryon_(nobaryon) , nooscenv_(0), retpart_(0) { zero_(); Init_(); } TransfertEisenstein::TransfertEisenstein(TransfertEisenstein& tf) : lp_(tf.lp_) ,Oc_(tf.Oc_) , Ob_(tf.Ob_) , h_(tf.h_) , tcmb_(tf.tcmb_) , nobaryon_(tf.nobaryon_) , nooscenv_(tf.nooscenv_), retpart_(tf.retpart_) { zero_(); Init_(); } 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. * h_, h2 = h_*h_; 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 = "< [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(unsigned short retpart) // To return only baryon or CDM part part of transfert function // retpart = 1 : return only CDM part of transfert function // retpart = 2 : return only Baryon part of transfert function // retpart = anything else: return only full transfert function // WARNING: only relevant for nobaryon_=false AND nooscenv!=2 { if(retpart!=1 && retpart!=2) retpart = 0; retpart_ = retpart; } double TransfertEisenstein::T0tild(double k,double alphac,double betac) { // Formule 10 p 608 //double q = k*th2p7_*th2p7_/(O0_*h_*h_); 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_*h_; // Calcul de Gamma_eff, formule 30 page 612 (pour fct lissee) if( nobaryon_==false && nooscenv_ == 2 ) gamma = O0_*h_*(alphag_ + (1.-alphag_)/(1.+pow(0.43*k*sfit_,4.))); // Gamma_eff // Formule 28 page 612 : qui est est equivalent a: // q = k / h_ * 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 / h_ * th2p7_*th2p7_ / gamma; // Mpc^-1 double q = k/(13.41*keq_) * (O0_*h_/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_ == 1) 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="<2) { cout<<"VarianceSpectrum::SetFilter: Error bad value for type of filter"<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_ == 1) 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 R,double kmin,double kmax) // Compute variance of spectrum pk_ by integration // Input: // R = taille du filter top-hat ou gaussien // kmin,kmax = bornes en k de l'integrale pour calculer la variance // Return: // valeur de la variance (sigma^2) // Remarque: // la meilleure approximation du filtre top-hat (R) est un filtre gaussien avec (Rg=R/sqrt(5)) // la variance renvoyee est la variance de la masse { if(R<=0. || kmin<=0 || kmax<=0. || kmin>=kmax) { cout<<"VarianceSpectrum::Variance: Error R<=0 or kmin<=0 or kmax<=0 or kmin>=kmax"<=kmax"); } R_ = R; double lkmin = log10(kmin), lkmax = log10(kmax); double var = IntegrateFuncLog(*this,lkmin,lkmax,dperc_,dlogkinc_,dlogkmax_,glorder_); return var; } //------------------------------------ double VarianceSpectrum::FindMaximum(double R,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: // R : taille du filter top-hat ou gaussien // kmin,kmax : intervalle de recherche // eps : precision requise sur les valeurs // Return: // position (en k) du maximum { if(R<=0. || kmin<=0 || kmax<=0. || kmin>=kmax) { cout<<"VarianceSpectrum::FindMaximum: Error R<=0 or kmin<=0 or kmax<=0 or kmin>=kmax || eps<=0"<=kmax || eps<=0"); } R_ = R; 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(R<=0. || kmin<=0 || kmax<=0. || kmin>=kmax || eps<=0.) { cout<<"VarianceSpectrum::FindLimits: Error R<=0 or kmin<=0 or kmax<=0 or kmin>=kmax or eps<=0"<=kmax || eps<=0"); } R_ = R; 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="<