!/===========================================================================/ ! Copyright (c) 2007, The University of Massachusetts Dartmouth ! Produced at the School of Marine Science & Technology ! Marine Ecosystem Dynamics Modeling group ! All rights reserved. ! ! FVCOM has been developed by the joint UMASSD-WHOI research team. For ! details of authorship and attribution of credit please see the FVCOM ! technical manual or contact the MEDM group. ! ! ! This file is part of FVCOM. For details, see http://fvcom.smast.umassd.edu ! The full copyright notice is contained in the file COPYRIGHT located in the ! root directory of the FVCOM code. This original header must be maintained ! in all distributed versions. ! ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ! THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ! PURPOSE ARE DISCLAIMED. ! !/---------------------------------------------------------------------------/ ! CVS VERSION INFORMATION ! $Id$ ! $Name$ ! $Revision$ !/===========================================================================/ !===========================================================================! ! n-dimentional optimal interpolation, Fortran module ! ! Modified from the original program optimal_interpolation.F which was ! ! written by Alexander Barth <abarth@marine.usf.edu> ! ! Dependencies: LAPACK (dsyev) ! !===========================================================================! MODULE MOD_OPTIMAL_INTERPOLATION # if defined (DATA_ASSIM) USE MOD_PREC USE CONTROL CONTAINS !--select the m observations closest to x SUBROUTINE SELECT_NEAREST(X,OX,PARAM,MM,INDEX,DISTANCE) IMPLICIT NONE REAL(SP),INTENT(IN) :: X(:),OX(:,:),PARAM(:,:) INTEGER, INTENT(IN) :: MM REAL(SP),INTENT(OUT) :: DISTANCE(MM) INTEGER, INTENT(OUT) :: INDEX(MM) REAL(SP) :: D(SIZE(OX,2)) INTEGER :: I DO I=1,SIZE(OX,2) D(I) = SUM(((X - OX(:,I)) * PARAM(:,I))**2) END DO CALL SORT(D,MM,INDEX) DISTANCE = D(INDEX) RETURN END SUBROUTINE SELECT_NEAREST !--returns the indexes of the m smallest elements in d SUBROUTINE SORT(D,MM,PANNIER) IMPLICIT NONE REAL(SP),INTENT(IN) :: D(:) INTEGER, INTENT(IN) :: MM INTEGER, INTENT(OUT) :: PANNIER(MM) INTEGER :: I,MAX_PANNIER(MM) DO I=1,MM PANNIER(I) = I END DO MAX_PANNIER = MAXLOC(D(PANNIER)) DO I=MM+1,SIZE(D) IF(D(I) < D(PANNIER(MAX_PANNIER(1))))THEN PANNIER(MAX_PANNIER(1)) = I MAX_PANNIER = MAXLOC(D(PANNIER)) END IF END DO RETURN END SUBROUTINE SORT SUBROUTINE OBSERVATION_COVARIANCE(OVAR,INDEX,R) IMPLICIT NONE REAL(SP),INTENT(IN) :: OVAR(:) INTEGER, INTENT(IN) :: INDEX(:) REAL(SP),INTENT(OUT) :: R(SIZE(INDEX),SIZE(INDEX)) INTEGER :: I R = 0.0_SP DO I=1,SIZE(INDEX) R(I,I) = OVAR(INDEX(I)) END DO RETURN END SUBROUTINE OBSERVATION_COVARIANCE FUNCTION BACKGROUND_COVARIANCE(X1,X2,PARAM) RESULT(C) IMPLICIT NONE REAL(SP),INTENT(IN) :: X1(:),X2(:),PARAM(:) REAL(SP) :: C REAL(SP) :: DIS(SIZE(X1)) !---> Lu Wang, R_Cressman@20230829 ! DIS = (X1 - X2)*PARAM ! ! C = EXP(-SUM(DIS**2)) DIS(1) = (X1(1)-X2(1))**2.0 + (X1(2)-X2(2))**2.0 C = 1.0/PARAM(1)**2.0 C = (C-DIS(1)) / (C+DIS(1)) IF (C<0) C = 0.0 !<--- END FUNCTION BACKGROUND_COVARIANCE !--compute pseudo-inverse SUBROUTINE PINV(A,TOLERANCE,WORK,C) IMPLICIT NONE REAL(SP),INTENT(IN) :: A(:,:),TOLERANCE,WORK(:) REAL(SP) :: C(SIZE(A,1),SIZE(A,1)) INTEGER :: I,J,K,INFO,NP REAL(SP) :: WP(SIZE(A,1)), UP(SIZE(A,1),SIZE(A,1)) NP = SIZE(A,1) UP = A # if defined (DOUBLE_PRECISION) CALL DSYEV('V','U', NP, UP,NP, WP, WORK, SIZE(WORK), INFO) # else CALL SSYEV('V','U', NP, UP,NP, WP, WORK, SIZE(WORK), INFO) # endif DO I=1,NP IF(WP(I) > TOLERANCE)THEN WP(I) = 1.0_SP/WP(I) ELSE WP(I) = 0.0_SP END IF END DO DO K=1,NP DO J=1,NP C(J,K) = 0.0_SP DO I=1,NP C(J,K) = C(J,K) + UP(J,I) * WP(I) * UP(K,I) END DO END DO END DO RETURN END SUBROUTINE PINV !--query the necessary workspace FUNCTION PINV_WORKSPACE(N) RESULT(LWORK) IMPLICIT NONE INTEGER,INTENT(IN) :: N INTEGER :: LWORK INTEGER :: INFO REAL(SP):: DUMMY,RWORK # if defined (DOUBLE_PRECISION) CALL DSYEV('V','U', N, DUMMY, N, DUMMY, RWORK, -1, INFO) # else CALL SSYEV('V','U', N, DUMMY, N, DUMMY, RWORK, -1, INFO) # endif LWORK = CEILING(RWORK) END FUNCTION !--main optimal interpolation routine SUBROUTINE OPTIMINTERP(OX,OF,OVAR,PARAM,MM,GX,GF,GVAR) IMPLICIT NONE REAL(SP),INTENT(IN) :: GX(:,:),OX(:,:),OF(:),OVAR(:),PARAM(:,:) INTEGER, INTENT(IN) :: MM REAL(SP),INTENT(OUT) :: GF(:),GVAR(:) REAL(SP) :: HPH(MM,MM), R(MM,MM), PH(MM), IA(MM,MM), PHIA(MM) INTEGER :: GN,INDEX(MM) REAL(SP) :: DISTANCE(MM) INTEGER :: I,J1,J2,LWORK REAL(SP) :: TOLERANCE = 1E-5 REAL(SP),ALLOCATABLE :: WORK(:) GN = SIZE(GX,2) !--query and allocate workspace for pseudo-inverse LWORK = PINV_WORKSPACE(MM) !$omp parallel private(work,i,iA,PHiA,index,distance,HPH,j1,j2) ALLOCATE(WORK(LWORK)) !$omp do DO I=1,GN !--get the indexes of the nearest observations CALL SELECT_NEAREST(GX(:,I),OX,PARAM,MM,INDEX,DISTANCE) !--form compute the error covariance matrix of the observation CALL OBSERVATION_COVARIANCE(OVAR,INDEX,R) !--form the error covariance matrix background field DO J1=1,MM DO J2=1,MM HPH(J1,J2) = & BACKGROUND_COVARIANCE(OX(:,INDEX(J1)),OX(:,INDEX(J2)),PARAM(:,INDEX(J2))) END DO PH(J1) = BACKGROUND_COVARIANCE(GX(:,I),OX(:,INDEX(J1)),PARAM(:,INDEX(J1))) END DO !--covariance matrix of the innovation IA = HPH + R !--pseudo inverse of the covariance matrix of the innovation CALL PINV(IA,TOLERANCE,WORK,IA) PHIA = MATMUL(PH,IA) !--compute the analysis GF(I) = DOT_PRODUCT(PHIA,OF(INDEX)) !--compute the error variance of the analysis GVAR(I) = 1. - DOT_PRODUCT(PHIA,PH) END DO !$omp end do DEALLOCATE(WORK) !$omp end parallel RETURN END SUBROUTINE OPTIMINTERP # endif END MODULE MOD_OPTIMAL_INTERPOLATION