diff --git a/prep_syndata/calc_error_cov.f90 b/prep_syndata/calc_error_cov.f90 new file mode 100644 index 0000000000000000000000000000000000000000..d091d2792caea26d62bb703e95dbf2b6ecd53e52 --- /dev/null +++ b/prep_syndata/calc_error_cov.f90 @@ -0,0 +1,209 @@ +!--------------------------------------------------------------------------------------- +! FLEXINVERT: calc_error_cov +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT 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 +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> calc_error_cov +!! +!! Purpose: Calculates the prior error covariance matrix for the state variables. +!! +!! Interface: +!! +!! Inputs +!! config - configuration settings data structure +!! files - file settings data structure +!! +!! Externals +!! dsyevr (LAPACK routine) +!! +!--------------------------------------------------------------------------------------- + +subroutine calc_error_cov(config, files, state) + + use mod_var + use mod_settings + use mod_tools + + implicit none + + type (config_t), intent (in) :: config + type (files_t), intent (in) :: files + real, dimension(nvar), intent (in out) :: state + + character(len=max_path_len) :: filename + character(len=max_path_len) :: rowfmt + character(len=max_name_len) :: varname, lonname, latname, timename + integer :: i, j, k, it, jt, nt, num, ix, jy, nb, nread + real(kind=8) :: jd + real, dimension(ndvar) :: xerr + real(kind=8), dimension(ndvar,ndvar) :: cov + real, dimension(nbox,nbox) :: corr + real, dimension(:,:,:), allocatable :: tmp + integer :: lwork, liwork + integer, dimension(:), allocatable :: isuppz + integer, dimension(:), allocatable :: iwork + real(kind=8), dimension(:), allocatable :: work + real(kind=8), dimension(:,:), allocatable :: zwork + real(kind=8), dimension(:), allocatable :: evaltmp + real(kind=8), dimension(:,:), allocatable :: evectmp + integer :: ierr, info, neval, ind + real, parameter :: smallnum=1.e-15 + real :: area + logical :: lexist + + ! read error from analysis + ! ------------------------ + + inquire(file=trim(files%path_output)//'analysis.nc',exist=lexist) + if ( lexist ) then + filename = trim(files%path_output)//'analysis.nc' + lonname = 'longitude' + latname = 'latitude' + timename = 'time' + jd = juldatei + ! prior error + varname = 'epri' + nread = 1 + allocate( tmp(nxregrid,nyregrid,1) ) + call read_ncdf(filename, & + varname, & + lonname, & + latname, & + timename,& + rllx, rlly, & + nxregrid, nyregrid, & + jd, nread, num, & + tmp) + tmp = tmp*numscale + xerr(:) = 0. + do jy = 1, nyregrid + area = grid_area(reg_lat(jy)+0.5*rdy, rdy, rdx) + do ix = 1, nxregrid + xerr(nbox_xy(ix,jy)) = xerr(nbox_xy(ix,jy)) + tmp(ix,jy,1)*area + end do + end do + xerr(:) = xerr(:)/area_box(:) + deallocate(tmp) + ! prior flux + varname = 'fpri' + nread = ntstate + allocate( tmp(nxregrid,nyregrid,ntstate) ) + call read_ncdf(filename, & + varname, & + lonname, & + latname, & + timename,& + rllx, rlly, & + nxregrid, nyregrid, & + jd, nread, num, & + tmp) + tmp = tmp*numscale + do nt = 1, ntstate + do jy = 1, nyregrid + area = grid_area(reg_lat(jy)+0.5*rdy, rdy, rdx) + do ix = 1, nxregrid + state((nt-1)*nbox+nbox_xy(ix,jy)) = state((nt-1)*nbox+nbox_xy(ix,jy)) + tmp(ix,jy,nt)*area + end do + end do + state((nt-1)*nbox+1:nt*nbox) = state((nt-1)*nbox+1:nt*nbox)/area_box(:) + end do + deallocate(tmp) + else + write(logid,*) 'ERROR: file analysis.nc not found' + stop + endif + + ! read correlations + ! ----------------- + + inquire(file=trim(files%path_output)//'correl.txt',exist=lexist) + if ( lexist ) then + write(logid,*) 'Reading spatial correlation from correl.txt' + open(100,file=trim(files%path_output)//'correl.txt',action='read',status='old',iostat=ierr) + write(rowfmt,'(A,I6,A)') '(',nbox,'(E11.4,1X))' + print*, rowfmt + do nb = 1, nbox + read(100,fmt=rowfmt) corr(nb,:) + end do + close(100) + else + write(logid,*) 'ERROR: cannot find correl.txt' + stop + endif + + ! calculate error covariance + ! -------------------------- + + ! spatial error covariance one time step + cov(:,:) = 0d0 + ! sub-daily resolution (no correlation between sub-daily time steps) + do nt = 1, 1 + do j = 1, nbox + do i = 1, nbox + cov((nt-1)*nbox+i,(nt-1)*nbox+j) = dble(corr(i,j))*dble(xerr((nt-1)*nbox+i))*dble(xerr((nt-1)*nbox+j)) + end do + end do + end do + + ! calculate eigendecomposition of cov + ! ----------------------------------- + + ! B = U*D*U^(T) + lwork = 26*ndvar + liwork = 10*ndvar + allocate( work(lwork), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR error_cov: not enough memory' + allocate( iwork(liwork), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR error_cov: not enough memory' + allocate( isuppz(2*ndvar), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR error_cov: not enough memory' + allocate( evectmp(ndvar,ndvar), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR error_cov: not enough memory' + allocate( evaltmp(ndvar), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR error_cov: not enough memory' + allocate( zwork(ndvar,ndvar), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR error_cov: not enough memory' + + zwork = cov + call dsyevr('V','A','L',ndvar,zwork,ndvar,1.,1.,1,1,0.,neval,evaltmp,evectmp,ndvar,isuppz,work,lwork,iwork,liwork,info) + if ( info.ne.0 ) then + write(logid,*) 'ERROR error_cov: eigenvalue decomposition' + stop + endif + + ! truncate eigenvalues + ! eigenvalues are sorted in ascending order + ind = minloc(evaltmp, dim=1, mask=evaltmp.ge.(trunc*maxval(evaltmp))) + write(logid,*) 'Truncating eigenvalues at: ',trunc*maxval(evaltmp) + neig = ndvar - ind + 1 + write(logid,*) 'Number of retained eigenvalues: ',neig + + allocate( evals(neig) ) + allocate( evecs(ndvar, neig) ) + evals = evaltmp(ind:ndvar) + evecs = evectmp(:,ind:ndvar) + deallocate( evaltmp ) + deallocate( evectmp) + + deallocate(work) + deallocate(zwork) + deallocate(iwork) + deallocate(isuppz) + + +end subroutine calc_error_cov +