!# This source file is part of code Pégase.3.0.1 (2019-02-21).
!# Copyright: Michel Fioc (Michel.Fioc@iap.fr), Sorbonne université, 
!# Institut d'astrophysique de Paris/CNRS, France.
!# 
!# Pégase.3.0.1 is governed by the CeCILL license under French law and abides 
!# by the rules of distribution of free software. You can use, modify and/or 
!# redistribute this software under the terms of the CeCILL license as circulated 
!# by CEA, CNRS and INRIA at "http://www.cecill.info". The text of this license
!# is also available in French and in English in directory "doc_dir/" of this
!# code.
!# 
!# As a counterpart to the access to the source code and to the rights to copy,
!# modify and redistribute it granted by the license, users are provided only
!# with a limited warranty, and the software's author, the holder of the
!# economic rights, and the successive licensors have only limited
!# liability. 
!# 
!# The fact that you are presently reading this means that you have had
!# knowledge of the CeCILL license and that you accept its terms.
!#====================================================================== 

module mod_cosmo

  use mod_types
  implicit none
  private
  public :: fn_cosmic_time, fn_redshift

contains

!#======================================================================
!# Cosmic time as a function of redshift, assuming Omega_m + Omega_Lambda = 1.

  function fn_cosmic_time(redshift)

    use mod_constants, only : H_0_unit
    use mod_read_cosmo_param, only : Omega_m, H_0
    implicit none
    real(CDR), intent(in) :: redshift
    real(CDR) :: fn_cosmic_time

!# Cosmic time = f(z) for a flat Universe with a cosmpological constant.
!# Density of radiation neglected with regard to that of matter.
!# Ref.: Peebles, "Principles of physical cosmology", ed. 1993, p. 317.

    fn_cosmic_time = 2*arsinh(sqrt((1-Omega_m)/Omega_m/(1+redshift)**3))/3/ &
         H_0/H_0_unit/sqrt(1-Omega_m)

  end function fn_cosmic_time

!#======================================================================

  function fn_redshift0(age, form_redshift)

    implicit none
    real(CDR), intent(in) :: age, form_redshift
    real(CDR) :: fn_redshift0
!#......................................................................
    real(CDR), parameter :: epsilon_redshift = 1.e-6
    real(CDR) :: redshift1, redshift2, redshift_mid, age_mid, t_for
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    redshift1 = -1
    redshift2 = form_redshift
    t_for = fn_cosmic_time(form_redshift)

    do
       redshift_mid = (redshift1 + redshift2)/2
       age_mid = fn_cosmic_time(redshift_mid) - t_for
       if (age > age_mid) then
          redshift2 = redshift_mid
       else
          redshift1 = redshift_mid
       endif
       if (redshift2 - redshift1 < epsilon_redshift) exit
    enddo
    fn_redshift0 = redshift_mid

  end function fn_redshift0

!#======================================================================

  function fn_redshift(cosmic_time)

    implicit none
    real(CDR), intent(in) :: cosmic_time
    real(CDR) :: fn_redshift
!#......................................................................
    real(CDR), parameter :: epsilon_redshift = 1.e-6
    real(CDR) :: redshift1, redshift2, redshift_mid, cosmic_time0, cosmic_time_mid
    real(CDR) :: scale_factor1, scale_factor2, scale_factor_mid
    logical :: use_scale_factor
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    cosmic_time0 = fn_cosmic_time(0._CDR)
    if (cosmic_time > cosmic_time0) then
       redshift1 = -1
       redshift2 = 0
       use_scale_factor = .false.
    else 
       scale_factor1 = 1
       scale_factor2 = 0
       redshift1 = 0
       redshift2 = huge(1._CDR)
       use_scale_factor = .true.
    endif

    do
       if (use_scale_factor) then
          scale_factor_mid = (scale_factor1 + scale_factor2)/2
          redshift_mid = 1/scale_factor_mid - 1
       else
          redshift_mid = (redshift1 + redshift2)/2
          scale_factor_mid = 1/(1 + redshift_mid) 
       endif
       cosmic_time_mid = fn_cosmic_time(redshift_mid)
       if (cosmic_time > cosmic_time_mid) then
          redshift2 = redshift_mid
          scale_factor2 = scale_factor_mid
       else
          redshift1 = redshift_mid
          scale_factor1 = scale_factor_mid
       endif
       if (redshift2 - redshift1 < epsilon_redshift) exit
    enddo
    fn_redshift = redshift_mid

  end function fn_redshift

!#======================================================================

  function arsinh(x)

    implicit none
    real(CDR), intent(in) :: x
    real(CDR) :: arsinh
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    arsinh = log(x + sqrt(x**2+1))

  end function arsinh

end module mod_cosmo
