!--------------------------------------- LICENCE BEGIN -----------------------------------
!Environment Canada - Atmospheric Science and Technology License/Disclaimer,
!                     version 3; Last Modified: May 7, 2008.
!This is free but copyrighted software; you can use/redistribute/modify it under the terms
!of the Environment Canada - Atmospheric Science and Technology License/Disclaimer
!version 3 or (at your option) any later version that should be found at:
!http://collaboration.cmc.ec.gc.ca/science/rpn.comm/license.html
!
!This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
!without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
!See the above mentioned License/Disclaimer for more details.
!You should have received a copy of the License/Disclaimer along with this software;
!if not, you can write to: EC-RPN COMM Group, 2121 TransCanada, suite 500, Dorval (Quebec),
!CANADA, H9P 1J3; or send e-mail to service.rpn@ec.gc.ca
!-------------------------------------- LICENCE END --------------------------------------


module rmatrix_mod 8,1
  ! Module to handle non diagonal observation error covariances matrices for assimilation
  ! of radiances
  ! S. Heilliette ARMA August 2013
  use mpivar_mod,  only  : mpi_myid
  use rttov_const, only  : errorstatus_success
  implicit none
  private
  save

  ! public variables
  public :: rmat_lnondiagr
  ! public subroutines
  public :: rmat_init,rmat_cleanup,rmat_readCMatrix,rmat_setFullRMatrix,rmat_sqrtRm1

 TYPE rmat_matrix
    SEQUENCE   
    REAL(8) ,pointer     :: Rmat(:,:)=>null()
    INTEGER ,pointer     :: listChans(:)=>null()
    INTEGER              :: nchans=0
 END TYPE rmat_matrix

  type(rmat_matrix),target,allocatable  :: R_inst(:) ! non diagonal Covariance matrices (R) for each instrument
  type(rmat_matrix),target,allocatable  :: C_inst(:) ! non diagonal Correlation matrices for each instrument
  type(rmat_matrix),target,allocatable  :: R_tovs(:) ! non diagonal R matrices used for the assimilation of all radiances
 
  logical :: rmat_lnondiagr

  contains


  subroutine rmat_init(nsensors,nobtovs) 1,1
   
    implicit none
    integer ,intent (in) :: nsensors,nobtovs
    integer :: nulnam,ierr
    integer ,external:: fnom,fclos
    namelist /NAMRMAT/rmat_lnonDiagR

    ! Default value for parameter rmat_lnondiagr, don't use interchannel correlation by default
    rmat_lnonDiagR=.false.

    ! Read the parameters from NAMRMAT
    nulnam=0
    ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
    read(nulnam,nml=namrmat,iostat=ierr)
    if(ierr.ne.0) call abort3d('rmat_init: Error reading namelist')
    if(mpi_myid.eq.0) write(*,nml=namrmat)
    ierr=fclos(nulnam)
    if (rmat_lnonDiagR) then
      allocate(R_inst(nsensors))
      allocate(C_inst(nsensors))
      allocate(R_tovs(nobtovs))
    endif

  end subroutine rmat_init


  subroutine rmat_cleanup() 1
    implicit none
    if (rmat_lnondiagr) then
      deallocate(R_inst)
      deallocate(C_inst)
      deallocate(R_tovs)
    endif
  end subroutine rmat_cleanup


  subroutine rmat_readCMatrix(instrument, sensor_id, ichan ) 1,2
   
    implicit none
#include "rttov_coeffname.interface"
    integer ,intent (in) :: instrument(3), sensor_id, ichan(:)

    character (len=64) :: filename
    integer :: err

    call rttov_coeffname (err, instrument, filename, "Cmat")

    if (err== errorstatus_success) then
      call rmat_readCMatrixByFileName(filename,C_inst(sensor_id), ichan )
    else
      write(*,*) "Unknown instrument ",instrument(:)
      call abort3d("rmat_read_C_matrix")
    end if
    
  end subroutine rmat_readCMatrix


  subroutine rmat_setFullRMatrix(sigma,sensor_id,offset) 2,1
    implicit none
   
    integer ,intent (in) :: sensor_id,offset
    real (8) ,intent (in) :: sigma(:)

    integer :: i,nchn,j,ii,jj,nsigma
    real (8) :: product

    write(*,*) "rmat_setFullRMatrix: "

    write(*,*) "sensor_id:",sensor_id

    nsigma = size( sigma )
    if (nsigma<1) then
      write(*,*) "rmat_setFullRMatrix: Strange sigma array size",nsigma
      write(*,*) "Please check !"
      return
    endif

    R_inst(sensor_id) % nchans  =  C_inst(sensor_id) % nchans
    if ( R_inst(sensor_id) % nchans==0) return  
    allocate( R_inst(sensor_id)%Rmat(R_inst(sensor_id) % nchans,R_inst(sensor_id) % nchans) )
    allocate( R_inst(sensor_id)%listChans(R_inst(sensor_id) % nchans) )
    
    R_inst(sensor_id)%listChans(:)=C_inst(sensor_id)%listChans(:)

    do i=1,C_inst(sensor_id)%nchans
      ii=R_inst(sensor_id)%listChans(i) + offset
      do j=1,C_inst(sensor_id)%nchans
        jj=R_inst(sensor_id)%listChans(j) + offset
        product=sigma(ii)*sigma(jj)
        if(product<=0.) then
          write(*,*) "Invalid variance: missing channel in stat_tovs !"
          write(*,*) ii,jj,offset,sigma(ii),sigma(jj)
          call abort3d('rmat_setFullRMatrix')
        endif
        R_inst(sensor_id)%Rmat(i,j)=product*C_inst(sensor_id)%Rmat(i,j)
      enddo
    enddo

  end subroutine rmat_setFullRMatrix



  subroutine rmat_readCMatrixByFileName(infile,C,liste_chan) 1,2
    implicit none
    character (len=*),intent(in) :: infile ! name of input file
    type(rmat_matrix),intent(inout) :: C    ! correlation matrix structure
    integer ,intent(in),optional :: liste_chan(:) ! list of requested channels (if missing will read all file content)

    integer :: i,j,iu,ierr,count,ich,nchn,nch
    integer ,external :: fnom,fclos
    real(8) :: x
    integer ,allocatable :: index(:)

    nchn=-1
    if (present(liste_chan)) then
      nchn=size(liste_chan)
    endif

    iu=0
    ierr=fnom(iu,trim(infile),'FTN+SEQ+R/O',0)
    if (ierr/=0) then
      write(*,*) "Cannot open "//trim(infile)
      call abort3d("rmat_readCMatrixByFileName")
    endif

    write(*,*) "rmat_readCMatrixByFileName: Reading "//trim(infile)
    
    read(iu,*) nch
    if (nchn==-1) then
      nchn=nch
    else
      if(nchn.gt.nch) then
        write(*,*) "Not enough channels in "//trim(infile)
        write(*,*) nchn,nch
        call abort3d("rmat_readCMatrixByFileName")
      endif
    endif
    allocate(index(nch))
    
    C%nchans=nchn
    allocate(C%Rmat(nchn,nchn))
    allocate(C%listChans(nchn))
    C%Rmat=0.d0
    do i=1,nchn
      C%Rmat(i,i)=1.d0
    enddo
    count=0
    index=-1
    do i=1,nch
      read(iu,*) ich
      if (present(liste_chan)) then
        bj:do j=1,nchn
          if (ich==liste_chan(j)) then
            count=count+1
            index(i)=j
            C%listChans(count)=ich
            exit bj
          endif
        enddo bj
      else
        index(i)=i
        C%listChans(i)=ich
        count=count+1
      endif
    enddo
    if (count/=nchn) then
      write(*,*) "Warning: Missing information in "//trim(infile)
      do j=1,nchn
        write(*,*) j,liste_chan(j) 
      enddo
      write(*,*) "Not important if there is no observation of this family"
    endif

    do
      read(iu,*,iostat=ierr) i,j,x
      if (ierr/=0) exit
      if (index(i)/=-1 .and. index(j)/=-1) then
        C%Rmat(index(i),index(j))=x
        C%Rmat(index(j),index(i))=x
      endif
    enddo

    ierr= fclos(iu)
    deallocate(index)

  end subroutine rmat_readCMatrixByFileName



  subroutine rmat_sqrtRm1(sensor_id,nsubset,x,y,list_sub,indexTovs) 2,3
    ! Apply the operator R**-1/2 to x
    ! result in y for the subset of channels specified
    ! in list_sub
    implicit none
    integer ,intent (in) :: sensor_id,nsubset
    integer ,intent(in)   :: list_sub(nsubset)
    real(8) ,intent(in)   :: x(nsubset)
    real(8) ,intent(out)  :: y(nsubset)
    integer  ,intent(in)  :: indexTovs

    real (8) :: Rsub(nsubset,nsubset),alpha,beta
    integer :: index(nsubset)
    integer :: i,j
    type(rmat_matrix),pointer :: R

    if (R_tovs(indexTovs)%nchans==0) then

      if (sensor_id>0 .and. sensor_id<=size(R_inst)) then
        R=>R_inst(sensor_id)
      else
        write(*,*) "invalid sensor_id",sensor_id,size(R_inst)
        call abort3d('rmat_sqrtRm1')
      end if

      index=-1
      do i=1,nsubset
        bj: do j=1,R%nchans
          if(list_sub(i)==R%listChans(j)) then
            index(i)=j
            exit bj
          endif
        enddo bj
      enddo
      if (any(index==-1)) then
        write(*,*) "Missing information for some channel !"
        write(*,*) list_sub(:)
        write(*,*) index(:)
        call abort3d('rmat_sqrtRm1')
      endif
      R_tovs(indexTovs)%nchans=nsubset
      allocate(R_tovs(indexTovs)%listChans(nsubset))
      R_tovs(indexTovs)%listChans(1:nsubset)=list_sub(1:nsubset)
      do j=1,nsubset
        do i=1,nsubset
          Rsub(i,j)=R%Rmat(index(i),index(j))
        enddo
      enddo
      ! Calculation of R**-1/2
      call tmg_start(98,'RMAT_MATSQRT')
      call matsqrt(Rsub,nsubset,-1.d0)
      call tmg_stop(98)
      allocate(R_tovs(indexTovs)%Rmat(nsubset,nsubset))
      do j=1,nsubset
        do i=1,nsubset
          R_tovs(indexTovs)%Rmat(i,j)=Rsub(i,j)
        enddo
      enddo
    endif

    call tmg_start(99,'RMAT_MATMUL')
    alpha=1.d0
    beta=0.d0
    y=0.d0
    ! Optimized symetric matrix vector product from Lapack
    call dsymv("L", nsubset, alpha, R_tovs(indexTovs)%Rmat, nsubset,x, 1, beta, y, 1)
    call tmg_stop(99)

  end subroutine rmat_sqrtRm1

end module rmatrix_mod