From ba949d27642d085937d7e7a91855a6d327a42e22 Mon Sep 17 00:00:00 2001
From: Philippe WAUTELET <philippe.wautelet@aero.obs-mip.fr>
Date: Fri, 20 Sep 2019 16:10:12 +0200
Subject: [PATCH] Philippe 20/09/2019: rewrite normalization of LES budgets

---
 src/MNH/mode_les_diachro.f90 | 443 +++++++++++++++--------------------
 1 file changed, 191 insertions(+), 252 deletions(-)

diff --git a/src/MNH/mode_les_diachro.f90 b/src/MNH/mode_les_diachro.f90
index ec13c0303..4c91d1eba 100644
--- a/src/MNH/mode_les_diachro.f90
+++ b/src/MNH/mode_les_diachro.f90
@@ -4,9 +4,10 @@
 !MNH_LIC for details. version 1.
 !-----------------------------------------------------------------
 ! Modifications
-! G. TANGUY 19/05/2014 : correctoin DATIME in case of time average
-!  Philippe Wautelet: 05/2016-04/2018: new data structures and calls for I/O
+!  G. Tanguy   19/05/2014: correct DATIME in case of time average
+!  P. Wautelet 05/2016-04/2018: new data structures and calls for I/O
 !  P. Wautelet 13/09/2019: budget: simplify and modernize date/time management
+!  P. Wautelet 20/09/2019: rewrite normalization of LES budgets
 !-----------------------------------------------------------------
 !#######################
 MODULE MODE_LES_DIACHRO
@@ -15,6 +16,8 @@ MODULE MODE_LES_DIACHRO
 USE MODD_LUNIT
 use modd_les_n, only: xles_dates, xles_times
 
+use mode_msg
+
 implicit none
 
 private
@@ -27,198 +30,78 @@ CONTAINS
 !---------------------------------------------------------------------
 !
 !########################################################
-SUBROUTINE MAKE_NORM(HUNIT, KC, ODIV, PA_NORM, PLES_NORM)
+subroutine Make_norm( pa_norm, ples_norm, kpower )
 !########################################################
-!
-USE MODD_PARAMETERS
-USE MODD_LES
-IMPLICIT NONE
-!
-CHARACTER(LEN=50),       INTENT(IN)    :: HUNIT     ! physical unit of field
-INTEGER,                 INTENT(IN)    :: KC        ! character counter
-LOGICAL,                 INTENT(INOUT) :: ODIV      ! flag to make a division
-REAL, DIMENSION(:,:,:,:),INTENT(INOUT) :: PA_NORM   ! normalized field
-REAL, DIMENSION(:),      INTENT(IN)    :: PLES_NORM ! normalization coefficient
 
-INTEGER :: JK ! z counter
-INTEGER :: JT ! time counter
-INTEGER :: JP ! process counter
-INTEGER :: JN ! variable number counter (larger than 1 only for scalar var.)
-CHARACTER(LEN=50) :: YUNIT
-!
-!------------------------------------------------------------------------
-!
-YUNIT=HUNIT
-!
-IF ( ANY(PLES_NORM(:)==0.) ) THEN
-  PA_NORM(:,:,:,:)=XUNDEF
-  ODIV=.FALSE.
-  RETURN
-END IF
+use modd_les,        only: nles_current_times, nles_k
+use modd_parameters, only: XUNDEF
 
-DO JN=1,SIZE(PA_NORM,4)
-  IF (YUNIT(KC+1:KC+1)=='3') THEN
-    IF (ODIV) THEN
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JN)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JN) = PA_NORM(JK,JT,JP,JN) * PLES_NORM(JT)**3
-          END DO
-        END DO
-      END DO
-    ELSE
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JN)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JN) = PA_NORM(JK,JT,JP,JN) / PLES_NORM(JT)**3
-          END DO
-        END DO
-      END DO
-    END IF
-  ELSE IF  (YUNIT(KC+1:KC+1)=='2') THEN
-    IF (ODIV) THEN
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JN)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JN) = PA_NORM(JK,JT,JP,JN) * PLES_NORM(JT)**2
-          END DO
-        END DO
-      END DO
-    ELSE
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JN)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JN) = PA_NORM(JK,JT,JP,JN) / PLES_NORM(JT)**2
-          END DO
-        END DO
-      END DO
-    END IF
-  ELSE
-    IF (ODIV) THEN
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JN)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JN) = PA_NORM(JK,JT,JP,JN) * PLES_NORM(JT)
-          END DO
-        END DO
-      END DO
-    ELSE
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JN)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JN) = PA_NORM(JK,JT,JP,JN) / PLES_NORM(JT)
-          END DO
-        END DO
-      END DO
-    END IF
-  END IF
-END DO
-!
-ODIV=.FALSE.
-!
-END SUBROUTINE MAKE_NORM
-!
-!###########################################################
-SUBROUTINE MAKE_NORM_SV(HUNIT, KC, ODIV, PA_NORM, PLES_NORM)
-!###########################################################
-!
-USE MODD_PARAMETERS
-USE MODD_LES
-IMPLICIT NONE
-!
-CHARACTER(LEN=50),       INTENT(IN)    :: HUNIT     ! physical unit of field
-INTEGER,                 INTENT(IN)    :: KC        ! character counter
-LOGICAL,                 INTENT(INOUT) :: ODIV      ! flag to make a division
-REAL, DIMENSION(:,:,:,:),INTENT(INOUT) :: PA_NORM   ! normalized field
-REAL, DIMENSION(:,:),    INTENT(IN)    :: PLES_NORM ! normalization coefficient
+real, dimension(:,:,:,:), intent(inout) :: pa_norm   ! normalized field
+real, dimension(:),       intent(in)    :: ples_norm ! normalization coefficient
+integer,                  intent(in)    :: kpower    ! normalization power
 
-INTEGER :: JK ! z counter
-INTEGER :: JT ! time counter
-INTEGER :: JP ! process counter
-INTEGER :: JSV! scalar variables counter
-CHARACTER(LEN=50) :: YUNIT
-!
-!-------------------------------------------------------------------
-!
-YUNIT=HUNIT
-!
-DO JSV=1,SIZE(PLES_NORM,2)
-  IF (ANY(PLES_NORM(:,JSV)==0.)) THEN
-    PA_NORM(:,:,:,JSV)=XUNDEF
-    CYCLE
-  END IF
-  IF (YUNIT(KC+1:KC+1)=='3') THEN
-    IF (ODIV) THEN
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JSV)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JSV) = PA_NORM(JK,JT,JP,JSV) * PLES_NORM(JT,JSV)**3
-          END DO
-        END DO
-      END DO
-    ELSE
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JSV)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JSV) = PA_NORM(JK,JT,JP,JSV) / PLES_NORM(JT,JSV)**3
-          END DO
-        END DO
-      END DO
-    END IF
-  ELSE IF  (YUNIT(KC+1:KC+1)=='2') THEN
-    IF (ODIV) THEN
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JSV)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JSV) = PA_NORM(JK,JT,JP,JSV) * PLES_NORM(JT,JSV)**2
-            END DO
-        END DO
-      END DO
-    ELSE
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JSV)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JSV) = PA_NORM(JK,JT,JP,JSV) / PLES_NORM(JT,JSV)**2
-            END DO
-        END DO
-      END DO
-    END IF
-  ELSE
-    IF (ODIV) THEN
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JSV)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JSV) = PA_NORM(JK,JT,JP,JSV) * PLES_NORM(JT,JSV)
-            END DO
-        END DO
-      END DO
-    ELSE
-      DO JP=1,SIZE(PA_NORM,3)
-        DO JT=1,NLES_CURRENT_TIMES
-          DO JK=1,NLES_K
-            IF (PA_NORM(JK,JT,JP,JSV)/=XUNDEF) &
-            PA_NORM(JK,JT,JP,JSV) = PA_NORM(JK,JT,JP,JSV) / PLES_NORM(JT,JSV)
-          END DO
-        END DO
-      END DO
-    END IF
-  END IF
-END DO
-!
-ODIV=.FALSE.
-!
-END SUBROUTINE MAKE_NORM_SV
+integer :: jk ! z counter
+integer :: jt ! time counter
+integer :: jp ! process counter
+integer :: jn ! variable number counter (larger than 1 only for scalar var.)
+
+if ( kpower == 0 ) return
+
+!normalization is not possible if some values are zero
+if ( any( ples_norm(: ) == 0. ) ) then
+  pa_norm(:, :, :, : ) = XUNDEF
+  return
+end if
+
+do jn = 1, size( pa_norm, 4 )
+  do jp = 1, size( pa_norm, 3 )
+    do jt = 1, nles_current_times
+      do jk = 1, nles_k
+        if ( pa_norm(jk, jt, jp, jn ) /= XUNDEF ) &
+          pa_norm(jk, jt, jp, jn ) = pa_norm(jk, jt, jp, jn ) * ples_norm(jt )**( -kpower )
+      end do
+    end do
+  end do
+end do
+
+end subroutine Make_norm
+
+!########################################################
+subroutine Make_norm_sv( pa_norm, ples_norm, kpower )
+!########################################################
+
+use modd_les,        only: nles_current_times, nles_k
+use modd_parameters, only: XUNDEF
+
+real, dimension(:,:,:,:), intent(inout) :: pa_norm   ! normalized field
+real, dimension(:,:),     intent(in)    :: ples_norm ! normalization coefficient
+integer,                  intent(in)    :: kpower    ! normalization power
+
+integer :: jk ! z counter
+integer :: jt ! time counter
+integer :: jp ! process counter
+integer :: jsv! scalar variables counter
+
+if ( kpower == 0 ) return
+
+!normalization is not possible if some values are zero
+do jsv = 1, size( ples_norm, 2 )
+  if ( any( ples_norm(:, jsv ) == 0. ) ) then
+    pa_norm(:, :, :, jsv) = xundef
+    cycle
+  end if
+
+  do jp = 1, size( pa_norm, 3 )
+    do jt = 1, nles_current_times
+      do jk = 1, nles_k
+        if ( pa_norm(jk, jt, jp, jsv) /= xundef ) &
+            pa_norm(jk, jt,jp, jsv ) = pa_norm(jk, jt, jp, jsv ) * ples_norm(jt, jsv )**( -kpower )
+      end do
+    end do
+  end do
+end do
+
+end subroutine Make_norm_sv
 !
 !     ###################################################
       SUBROUTINE LES_NORM_4D(HUNIT, PA_LES, PA_NORM, OSV)
@@ -267,69 +150,125 @@ LOGICAL, OPTIONAL,           INTENT(IN)  :: OSV     ! flag for scalar variables
 !
 !       0.2  declaration of local variables
 !
-INTEGER :: JC    ! character counter
-LOGICAL :: GDIV  ! flag to make a division
-INTEGER :: IKG   ! number of 'kg' in the field unit
-!
-CHARACTER(LEN=50) :: YUNIT
-!
+integer, parameter :: NMAXUNITS = 10
+
+integer, parameter :: NNORM_K  = 1
+integer, parameter :: NNORM_KG = 2
+integer, parameter :: NNORM_M  = 3
+integer, parameter :: NNORM_PA = 4
+integer, parameter :: NNORM_S  = 5
+integer, parameter :: NNORM_RV = 6
+integer, parameter :: NNORM_SV = 7
+integer, parameter :: NNORMS   = 7
+
+integer :: idx, ispace
+integer :: ikg   ! number of 'kg' in the field unit
+integer :: inunits
+integer :: ipower
+integer :: ipower_kg_1st
+integer :: ji
+integer, dimension ( NNORMS ) :: ipowers
+character( len = 8 ) :: yun, yname, ypower
+character( len = 8 ), dimension( NMAXUNITS ) :: yunits
+logical :: gsv
 !------------------------------------------------------------------------------
-YUNIT=HUNIT//'                                              '
-!
-PA_NORM = PA_LES
-!
-IKG=0
-!
-DO JC=1,50
-  IF (YUNIT(JC:JC)=='g') THEN
-    IKG=IKG+1
-  ELSE IF (YUNIT(JC:JC)==' ') THEN
-    EXIT
-  END IF
-END DO
-!
-GDIV=.FALSE.
-!
-DO JC=1,49
-  !
-  SELECT CASE (YUNIT(JC:JC))
-    CASE('m')
-      CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_M)
-    CASE('g')
-      IF (IKG==1) THEN
-        CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_RHO)
-      ELSE
-        IF (PRESENT(OSV)) THEN
-          IF (OSV) THEN
-            IF (.NOT. GDIV) &
-            CALL MAKE_NORM_SV(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_SV)
-            GDIV=.FALSE.
-          ELSE
-            IF (.NOT. GDIV) &
-            CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_RV)
-            GDIV=.FALSE.
-          END IF
-        ELSE 
-          IF (.NOT. GDIV) &
-          CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_RV)
-          GDIV=.FALSE.
-        END IF
-      END IF
-    CASE('K')
-      CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_K)
-    CASE('s')
-      CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_S)
-    CASE('a')
-      CALL MAKE_NORM(YUNIT, JC, GDIV, PA_NORM, XLES_NORM_P)
-    CASE('/')
-      GDIV=.TRUE.
-    CASE(' ')
-      EXIT
-  END SELECT
-END DO
-!
-WHERE(PA_NORM==XUNDEF) PA_NORM = PA_LES
-!
+
+gsv = .false.
+if ( present( osv ) ) gsv = osv
+
+pa_norm(:, :, :, : ) = pa_les(:, :, :, : )
+
+!Parse units
+!Each unit is separated by blanks
+!First part: unit, second part: power (and sign of it)
+ipowers(: ) = 0
+inunits = 0
+ikg = 0
+idx = 1
+
+!Separate units
+do
+  ispace = scan( hunit(idx: ), ' ' )
+  if ( ispace == 0 ) then
+    inunits = inunits + 1
+    if (inunits > NMAXUNITS ) call Print_msg( NVERB_FATAL, 'GEN', 'LES_NORM_4D', 'inunits > NMAXUNITS' )
+    yunits(inunits ) = hunit(idx:)
+    exit
+  else if ( ispace == len(hunit(idx: )) ) then
+    exit
+  else
+    inunits = inunits + 1
+    if (inunits > NMAXUNITS ) call Print_msg( NVERB_FATAL, 'GEN', 'LES_NORM_4D', 'inunits > NMAXUNITS' )
+    yunits(inunits ) =  hunit( idx : idx+ispace-1 )
+    idx = idx + ispace
+  end if
+end do
+
+!Treat units and their power
+!kg are special: they can appear twice with opposite power signs (kg kg-1)
+!In that case, they are normalized with xles_norm_rv or xles_norm_sv
+do ji = 1, inunits
+  yun = yunits(ji )
+
+  !Non dimensional unit
+  if ( trim( yun ) == '-' .or. trim( yun ) == '1' .or. trim( yun ) == 'percent') then
+    cycle
+  end if
+
+  !Separate unit and its power
+  idx = scan( yun, '-1234567890' )
+  if ( idx == 0 ) then
+    yname = trim( yun )
+    ypower = ''
+    ipower = 1
+  else
+    yname  = yun( 1 : idx - 1 )
+    ypower = yun( idx : )
+    read (ypower,'(I8)') ipower
+  end if
+
+  select case( trim( yname ) )
+    case ( 'K' )
+      ipowers(NNORM_K  ) = ipowers(NNORM_K  ) + ipower
+    case ( 'kg' )
+      ikg = ikg + 1
+      if ( ikg == 1 ) ipower_kg_1st = ipower
+      ipowers(NNORM_KG ) = ipowers(NNORM_KG ) + ipower
+    case ( 'm' )
+      ipowers(NNORM_M  ) = ipowers(NNORM_M  ) + ipower
+    case ( 'Pa' )
+      ipowers(NNORM_PA ) = ipowers(NNORM_PA ) + ipower
+    case ( 's' )
+      ipowers(NNORM_S  ) = ipowers(NNORM_S  ) + ipower
+    case default
+      call Print_msg( NVERB_WARNING, 'IO', 'LES_NORM_4D', 'unknown unit: '//trim(yname)//'. Conversion could be wrong.' )
+  end select
+end do
+
+if ( ikg > 1 .and. ipowers(NNORM_KG ) /= 0 ) &
+  call Print_msg( NVERB_ERROR, 'IO', 'LES_NORM_4D', 'if kg appears more than one time, it should be admimensional' )
+
+if ( ikg > 2 ) &
+  call Print_msg( NVERB_ERROR, 'IO', 'LES_NORM_4D', 'kg should not appear more than 2 times' )
+
+if ( ikg == 2 ) then
+  if ( gsv ) then
+    ipowers(NNORM_SV ) = ipower_kg_1st
+  else
+    ipowers(NNORM_RV ) = ipower_kg_1st
+  end if
+end if
+
+if (ipowers(NNORM_K  ) /= 0 ) call Make_norm   ( pa_norm, xles_norm_k,   ipowers(NNORM_K  ) )
+if (ipowers(NNORM_KG ) /= 0 ) call Make_norm   ( pa_norm, xles_norm_rho, ipowers(NNORM_KG ) )
+if (ipowers(NNORM_M  ) /= 0 ) call Make_norm   ( pa_norm, xles_norm_m,   ipowers(NNORM_M  ) )
+if (ipowers(NNORM_PA ) /= 0 ) call Make_norm   ( pa_norm, xles_norm_p,   ipowers(NNORM_PA ) )
+if (ipowers(NNORM_S  ) /= 0 ) call Make_norm   ( pa_norm, xles_norm_s,   ipowers(NNORM_S  ) )
+if (ipowers(NNORM_RV ) /= 0 ) call Make_norm   ( pa_norm, xles_norm_rv,  ipowers(NNORM_RV ) )
+if (ipowers(NNORM_SV ) /= 0 ) call Make_norm_sv( pa_norm, xles_norm_sv,  ipowers(NNORM_SV ) )
+
+where( pa_norm == XUNDEF ) pa_norm = pa_les
+
 END SUBROUTINE LES_NORM_4D
 !
 !------------------------------------------------------------------------------
-- 
GitLab