C**********************************************************************
C  program UV Index
C**********************************************************************
C  program will read fixed fields
C  get date , cycle and fcst hour
C  determine Solar Zenith Angles
C  Read ozone field
C  From LUT (ozone, sza) determine Erythemal Dose Rates
C  Determine which GADS aerosol fields to use
C  Read GEFS_aer AOD and SSA
C  Call Erythemal_UV inputs:AOD,SSA,SZA output:Ratio
C  Adjust field for Elevation
C  Read SNOD (snow depth)
C  Read ALBDO (albedo) files
C  Determine where snow depth is greater than 0.02 (~1 inch) then use
C  the albdo 
C  Adjust field for Albedo (Snow)
C  Read UVBCLR and UVBCLD fields
C  Determine 2nd 3 hr UVBCLR AND UVBCLD
C  Determine 2nd 3 hr UV Transmissivity
C  Adjust field for UV Trans (Clouds)
C
C   OUTPUT FILES:
C     fort.052 - UV Index clrsky field (GRIB)
C
C   SUBPROGRAMS CALLED: (LIST ALL CALLED FROM ANYWHERE IN CODES)
C     UNIQUE:    - ROUTINES THAT ACCOMPANY SOURCE FOR COMPILE
C       grib     - grib UV Index field
C     LIBRARY:
C       W3LIB    - link to /nwprod/w3lib
C
C ATTRIBUTES:
C   LANGUAGE: FORTRAN
C   MACHINE:  Linux
C
C   Changes: 2009-02-25 C.Long Changed IFCST = 24
C                            to     IFCST = FCSTHR
C            2012-12-03 C.Long Changed open calls to read symbolic linked
C                          file names
C            2016-04-13 C.Long Changed to read hourly files
C            2018-03-01 C.Long Change to use SNOD and ALBDO
C            2020-02-13 C.Long Made code ready to use GEFS AOD and SSA
C            2021-07-09 Hai-Tien Lee 
C                     add UVIUSC text and grib output
C                     debug - avoid negative ALBADJ=A0 when ALB=0
C            2022-01-24 Hai-Tien Lee
C                     calculate UVI with 0.25deg fields for both Input and Output
C$$$
C-----------------------------------------------------------
C 
      PARAMETER (IGRD=1440, JGRD=721, NXY=IGRD*JGRD)
      PARAMETER (MXBIT=32,LENPDS=28,LENGDS=32)
      PARAMETER (MXSIZE=30+LENPDS+LENGDS+NXY*(MXBIT+1)/8)
C
      INTEGER YYYYMMDD, EDATE, YYYY, MM, DD, DOY, DPY, UTC, CYCL,
     > FCSTHR, YY, XHR, YHR
C
      REAL OZONE(igrd,jgrd), SZA(igrd,jgrd), GLAT,
     > EDRLUT(18,27), ELEV(igrd,jgrd), UV(NXY), 
     > UVBCLD(igrd,jgrd),  UVBCLR(igrd,jgrd), ALBEDO(igrd,jgrd),
     > UVBCLDx(igrd,jgrd), UVBCLRx(igrd,jgrd), ALBEDOx(igrd,jgrd),
     > UVBCLDy(igrd,jgrd), UVBCLRy(igrd,jgrd), ALBEDOy(igrd,jgrd),
     > ELADJ(igrd,jgrd), EL(igrd,jgrd),
     > ALBSNO(igrd,jgrd), SNOD(igrd,jgrd)
C
      REAL ERY1(igrd,jgrd), ERY2(igrd,jgrd), ERY3(igrd,jgrd),
     >     ERY4(igrd,jgrd), ERY5(igrd,jgrd), ERYOUT(igrd,jgrd)
      REAL OPT(igrd,jgrd), SSA(igrd,jgrd)
      REAL OPTS(igrd,jgrd), SSAS(igrd,jgrd)
      REAL OPTW(igrd,jgrd), SSAW(igrd,jgrd)
      REAL MON(12), MONL(12)
      REAL UVTRANS(igrd,jgrd), AERO(igrd,jgrd)
      
      REAL UVIUCS(igrd,jgrd),UVC(NXY)
C
      REAL PI, D2R, R2D, FZ, FC, SOLDEC
C
      INTEGER IHR, IFCST, IDSCALE, IDMODEL, IDPARA, IDLEVEL, ITOT
C
      CHARACTER*1 KBUF(MXSIZE)
C
      DATA MON/0,31,59,90,120,151,181,212,243,273,304,334/
      DATA MONL/0,31,60,91,121,152,182,213,244,275,305,335/
C
      COMMON /COMGRIB/YY,MM,DD,IHR,IFCST,IDSCALE,IDMODEL,IDPARA,
     &     IDLEVEL,ITOT
C
C-----------------------------------------------------------------------
C
      PI = 3.1415927
      D2R = PI/180.0
      R2D = 180.0/PI
C
C...Read Input
C
      READ(5,*) YYYYMMDD 
      READ(5,*) CYCL
      READ(5,*) FCSTHR
      READ(5,*) XHR
      READ(5,*) YHR
C
      WRITE(6,910) YYYYMMDD, CYCL, FCSTHR, XHR, YHR
C
      YYYY = INT(YYYYMMDD/10000)
      YY   = MOD(YYYY,100)
      MM   = INT((YYYYMMDD-YYYY*10000)/100)
      DD   = YYYYMMDD-YYYY*10000-MM*100
C
C...Read Fixed Fields
C
C...Read Erythemal(oz,sza) LUT 
C...FILE='ery_lut_fix'
      READ(10,*) EDRLUT
C      write(6,911) ((edrlut(i,j),i=1,18),j=1,27)
      WRITE(6,FMT='("Erythemal LUT read")')
C
C...Read Elevation
C
C...FILE='orograp_fix'
      READ(11,907) ELEV
C 
      WRITE(6,FMT='("Elevation data read")')
C
C...Read Input Fields (1,1) is 90North, 0East
C
C...FILE='albdox_in'
      READ(22,*) nx1,ny1 
      READ(22,*) ALBEDOx
      WRITE(6,FMT='("Albedo_x Data Read",i4,i4)') nx1,ny1
C
C...FILE='uvclrx_in'
      READ(23,*) nx2, ny2 
      READ(23,*) UVBCLRx
      WRITE(6,FMT='("UVBCLR_x Data Read",i4,i4)') nx2,ny2
C
C...FILE='uvcldx_in'
      READ(24,*) nx3, ny3
      READ(24,*) UVBCLDx
      WRITE(6,FMT='("UVBCLD_x Data Read",i4,i4)') nx3,ny3
C
C...FILE='uvozoy_in'
      READ(30,*) nx0, ny0
      READ(30,*) OZONE
      WRITE(6,FMT='("Ozone Data Read",i4,i4)') nx0, ny0
C
C...FILE='snod_in'
      READ(31,*) nx1,ny1
      READ(31,*) SNOD
      WRITE(6,FMT='("Snow Depth Read",i4,i4)') nx1,ny1
C
C...FILE='albdoy_in'
      READ(32,*) nx1,ny1
      READ(32,*) ALBEDOy
      WRITE(6,FMT='("Albedo_y Read",i4,i4)') nx1,ny1
C
C...FILE='uvclry_in'
      READ(33,*) nx2, ny2 
      READ(33,*) UVBCLRy
      WRITE(6,FMT='("UVBCLR_y Data Read",i4,i4)') nx2,ny2
C
C...FILE='uvcldy_in'
      READ(34,*) nx3, ny3
      READ(34,*) UVBCLDy
      WRITE(6,FMT='("UVBCLD_y Data Read",i4,i4)') nx3,ny3
C
C Read GEFS Optical Thickness and SSA
C
C...FILE='opt_in'
      READ(36,*) nx3, ny3
      READ(36,*) OPT 
      WRITE(6,FMT='("AOD Data Read",i4,i4)') nx3,ny3
C
C...FILE='ssa_in'
      READ(37,*) nx3, ny3
      READ(37,*) SSA 
      WRITE(6,FMT='("SSA Data Read",i4,i4)') nx3,ny3
C--------------------------------------------------------------
C
C...Create X hour file from Y hour files
C
C...h6 = 6*6h-5*5h
C...h5 = 5*5h-4*4h
C...h4 = 4*4h-3*3h
C...h3 = 3*3h-2*2h
C...h2 = 2*2h-1*1h
C...h1 = 1h
C
      IF (XHR .GT. 0) THEN
         DO 50 J = 1, JGRD
            DO 49 I = 1, IGRD
               IF (SNOD(I,J) .LT. 0.01 .OR. SNOD(I,J) .GT. 5.0) THEN
                  SNOD(I,J)=-1.0
               ENDIF
C...ALBEDO averages are accumulated over the 6 hour forecast period
               ALBx = ALBEDOx(I,J)
               ALBy = ALBEDOy(I,J)
               IF (ALBx .GE. 0.0) THEN
                  ALBEDO(I,J) = FLOAT(YHR)*ALBy - FLOAT(XHR)*ALBx
               ELSE
                  ALBEDO(I,J) = ALBy
               ENDIF

               IF ((SNOD(I,J) .GT. 0.02 .AND. SNOD(I,J).LT.5) .and. 
     >(ALBEDO(I,J).GT.0.0 .AND. ALBEDO(I,J).LE.100)) THEN
                  ALBSNO(I,J) = ALBEDO(I,J)
               ELSE
                  ALBSNO(I,J) = -1.0
               ENDIF
C...DUVB and CDUVB averages are accumulated over the 6 hour forecast period
               CLDX = UVBCLDX(I,J)
               CLDY = UVBCLDY(I,J)
               IF (CLDX .GE. 0.0) THEN
                  UVBCLD(I,J) = FLOAT(YHR)*CLDY - FLOAT(XHR)*CLDX
               ELSE
                  UVBCLD(I,J) = CLDY
               ENDIF
C
               CLRX = UVBCLRX(I,J)
               CLRY = UVBCLRY(I,J)
               IF (CLRX .GE. 0.0) THEN
                  UVBCLR(I,J) = FLOAT(YHR)*CLRY - FLOAT(XHR)*CLRX
               ELSE
                  UVBCLR(I,J) = CLRY
               ENDIF
   49       CONTINUE
   50    CONTINUE
      ELSE
         DO 52 J = 1, JGRD
            DO 51 I = 1, IGRD
               IF (SNOD(I,J) .LT. 0.01 .OR. SNOD(I,J).GT.5.0) THEN
                  SNOD(I,J)=-1.0
               ENDIF
C
               ALBEDO(I,J) = ALBEDOy(I,J)
C
C...Check to see if snow depth and albedo values are reasonable
C
               IF ((SNOD(I,J) .GT. 0.02 .AND. SNOD(I,J).LT.5) .AND.
     >            (ALBEDO(I,J).GT.0 .AND. ALBEDO(I,J).LE.100)) THEN
                  ALBSNO(I,J) = ALBEDO(I,J)
               ELSE
                  ALBSNO(I,J) = -1.0
               ENDIF
C
               UVBCLD(I,J) = UVBCLDY(I,J)
               UVBCLR(I,J) = UVBCLRY(I,J)
   51       CONTINUE
   52    CONTINUE
      ENDIF

C...Determine DOY and Days Per Year
C
      IF (MOD(YYYY,4).EQ.0) THEN
         DOY = MONL(MM) + DD
         DPY = 366
      ELSE
         DOY = MON(MM) + DD 
         DPY = 365
      ENDIF
      WRITE(6,919) YYYY, MM, DD, DOY
C
C...from the cycle time and forecast hour 
C...determine the day and UTC time of the product
C
      FC = FLOAT(CYCL)
      FZ = FLOAT(FCSTHR)
      UTC = MOD((CYCL+FCSTHR),24)
C
      DOY = DOY + (CYCL+FCSTHR)/24
      WRITE(6,920) DOY, CYCL, FCSTHR, UTC
C
C...determine Solar Declination
C
      SOLDEC = 23.45*SIN(2.0*PI*(DOY-80.0)/DPY)
c.      WRITE(6,921) SOLDEC
      SOLDEC = D2R*SOLDEC
C
C...determine Solar Zenith Angle
C
C     GLON = 360.0/igrd
C     GLAT = 180.0/(jgrd-1)
      GLON = 0.25
      GLAT = 0.25
C
      DO 10 IL = 1, jgrd
         RLT = 90.0 - (IL-1)*GLAT
         RLAT = D2R*RLT   
         DO 9 IJ = 1, igrd
            RLON = (IJ-1)*GLON
            HRR  = (UTC-12.0)*15.0 + RLON
            HOUR = D2R*HRR 
            COSZ = SIN(SOLDEC)*SIN(RLAT)+COS(SOLDEC)*COS(RLAT)*COS(HOUR)
            Z = R2D*ACOS(COSZ)
c...only work with SZA smaller than 85 degrees
            IF (Z.GE.0.0 .AND. Z.LE.85.0) THEN
               SZA(IJ,IL) = Z
            ELSE
               SZA(IJ,IL) = -1.0
            ENDIF
    9    CONTINUE
c.      WRITE(6,923)il,rlt, MAXVAL(SZA(1:igrd,il)), 
c.     > MINVAL(SZA(1:igrd,il), MASK = SZA(1:igrd,il) .GT. 0)
   10 CONTINUE
C
C...DETERMIN EARTH-SUN DISTANCE RATIO
C
      CALL SUNDIS(YYYYMMDD, ESRAT)
      WRITE(6,908) ESRAT
C
C...DETERMINE SEA LEVEL, NO AEROSOL, NO SNOW, NO CLOUDS EDR
C
      DO 20 LTT = 1, jgrd
         DO 19 LNN = 1, igrd 
            IF (SZA(LNN,LTT).GT.0.0 .AND. SZA(LNN,LTT).LT.85.0) THEN
               OZ = 1+(OZONE(LNN,LTT)-80.0)/20.0
               SZ = 1+SZA(LNN,LTT)/5.0
               CALL W3FT01(SZ, OZ, EDRLUT, EOUT, 18, 27, 0, 1)
               ERY1(LNN,LTT) = EOUT*ESRAT
C
C              if (lnn.eq.576) write(6,940)glat(ltt),ozone(lnn,ltt),
C     > oz, sza(lnn,ltt), sz, eout  
C
c.        if(lnn.eq.576) write(6,945) glat(ltt),sza(lnn,ltt),
c.     > ozone(lnn,ltt),albedo(lnn,ltt),uvbclr(lnn,ltt),uvbcld(lnn,ltt)
            ENDIF
   19    CONTINUE
c.      WRITE(6,924)ltt,glat(ltt), MAXVAL(ERY1(1:igrd,ltt)), 
c.     > MINVAL(ERY1(1:igrd,ltt))
   20 CONTINUE
C 
C...ADJUST Arrays FOR ELEVATION
C...Array operations
C
      EL = ELEV/1000.0
      ELADJ = -0.0009 + 5.4457*EL - 0.0414*EL**2
      ERY2 = ERY1*(1.0+ELADJ/100.0)
c.      DO 61 LTT = 1, JGRD
c.      WRITE(6,926)ltt,glat(ltt), MAXVAL(ERY2(1:igrd,ltt)), 
c.     > MINVAL(ERY2(1:igrd,ltt))
c.   61 CONTINUE

C Assign UVIUCS as ERY2 (astro, ozone, elevation adjusted UVI)
      UVIUCS=ERY2
      
C
C...Adjust for AOD and SSA
C...Compute Erythemal Ratio With vs W/O Aerosols
C
      DO 60 LTT = 1, jgrd
         LTX = JGRD - LTT + 1
         DO 59 LNN = 1, igrd
            IF (SZA(LNN,LTT).GT.0.0 .AND. SZA(LNN,LTT).LT.85.0) THEN
              CALL ERYTH(OPT(LNN,LTT),SSA(LNN,LTT),SZA(LNN,LTT),RAT)
            ELSE
               RAT = 0.0
            ENDIF
C
            AERO(LNN,LTT) = RAT*100.0
            ERY3(LNN,LTT) = ERY2(LNN,LTT)*RAT
   59    CONTINUE
C      WRITE(6,925)ltt,glat(ltt), MAXVAL(ERY3(1:igrd,ltt)), 
C     > MINVAL(ERY3(1:igrd,ltt))
   60 CONTINUE
C
C...ADJUST FOR ALBEDO (SNOW)
C
      ERY4 = ERY3
C
      A0 = -1.32278
      A1 =  0.43339
      A2 =  0.00053
      A3 =  0.00002
C
C...Adjust for snow if snow dept is greater than 0.02m
C
      DO 80 LTT = 1, JGRD
         N = 0
         NN = 0
         DO 79 LNN = 1, IGRD
            ALB = ALBSNO(LNN,LTT)
C
            IF (SZA(LNN,LTT).GT.0 .AND. SZA(LNN,LTT).LT.85) THEN
C 2021709 Hai-Tien Lee debug - avoid negative ALBADJ=A0 when ALB=0
               if (ALB .gt. 0.) then
                  ALBADJ = A3*ALB**3 + A2*ALB**2 + A1*ALB + A0
               else
                  ALBADJ=0.
               endif
               ERY4(LNN,LTT) = ERY3(LNN,LTT)*(1.0 + ALBADJ/100.0)
               UVIUCS(LNN,LTT) = ERY2(LNN,LTT)*(1.0 + ALBADJ/100.0)
               N = N + 1
               NN = NN + 1
            ENDIF
   79    CONTINUE
c.      WRITE(6,927)ltt,glat(ltt), MAXVAL(ERY4(1:igrd,ltt)), 
c.     > MINVAL(ERY4(1:igrd,ltt)), N, NN
   80 CONTINUE
C
C...Determine UV Trans
C
      ERY5 = ERY4
c
      DO 90 LTT = 1, JGRD
         DO 89 LNN = 1, IGRD
C23456789012345678901234567893123456789412345678951234567896123456789712
            IF(UVBCLD(LNN,LTT).GT.0.0.AND.UVBCLR(LNN,LTT).GT.0.0)THEN
               UVT = UVBCLD(LNN,LTT)/UVBCLR(LNN,LTT)
               IF (UVT .GT. 1.0) UVT = 1.0
               ERY5(LNN,LTT) = ERY4(LNN,LTT)*UVT
            ELSE
               UVT = -0.01
            ENDIF
            UVTRANS(LNN,LTT) = UVT*100.0
   89    CONTINUE
c.      WRITE(6,928)j,glat(j), MAXVAL(ERY5(1:igrd,j)), 
c.     > MINVAL(ERY5(1:igrd,j))
   90 CONTINUE
C
C...changed from ery1 to ery2 August 25, 2005
C
      WRITE(51,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(51,902) ERY2
C
      WRITE(52,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(52,902) ERY5
C
      WRITE(53,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(53,902) UVTRANS
C
      WRITE(54,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(54,902) SZA 
C
      WRITE(55,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(55,902) AERO
C
      WRITE(56,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(56,902) ALBEDO
C
      WRITE(57,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(57,902) ALBSNO
C
      WRITE(58,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(58,902) OZONE
C
      WRITE(59,909) YYYYMMDD, CYCL, FCSTHR
      WRITE(59,902) SNOD

C add UVIUCS output to LUN=60
C      WRITE(60,909) YYYYMMDD, CYCL, FCSTHR
C      WRITE(60,902) UVIUCS
C Experimental output
C     WRITE(41,909) YYYYMMDD, CYCL, FCSTHR
C     WRITE(41,902) ERY1
C     WRITE(42,909) YYYYMMDD, CYCL, FCSTHR
C     WRITE(42,902) ERY3
C     WRITE(43,909) YYYYMMDD, CYCL, FCSTHR
C     WRITE(43,902) ERY4
C
C...GRIB PARAMETERS
C     IYY, IMM, IDD ARE PROVIDED BELOW
      IHR=12
      IFCST=FCSTHR
      IDSCALE=0
      IDMODEL=2
      IDPARA=206
      IDLEVEL=1
C...MODEL ID= 2, UV INDEX
C...PARAMETER ID=206, UV INDEX (unitless)
C...TYPE OF LEVEL=  1, SURFACE
C...SCALING FACTOR
C
C...Grib UV field as a product (Convert unit to Grib standard: W/m**2)
C
C 20230620 Hai-Tien Lee
C Note that the UV Index is unitless. It is obtained by scaling
C the erythemal-weighted UV radiative flux by 25 mW/m^2.  
C This program, however, encodes UVI at flux unit "mW/m^2".

      UVMAX = MAXVAL(ERY5)
      UVMIN = MINVAL(ERY5)
      WRITE(6,913) UVMAX, UVMIN

      do j = 1, jgrd
         do i = 1, igrd
            k = (j-1)*igrd + i
            uv(k) = ery5(i,j)*1.e-3
         enddo
      enddo
C
      WRITE(6,912) MXSIZE
      CALL GRIB(uv, kbuf)
C
      open(62,status='replace',form='unformatted',access='direct',
     > recl=itot)
      WRITE(62,rec=1) (KBUF(i),i=1,ITOT)
C
C Add UVIUSC grib output
      do j = 1, jgrd
         do i = 1, igrd 
            k = (j-1)*igrd + i
            UVC(k) = UVIUCS(i,j)*1.e-3
         enddo
      enddo
C
      WRITE(6,912) MXSIZE
      CALL GRIB(UVC, KBUF)
C
      OPEN(63, status='replace', form='unformatted',access='direct',
     > recl=itot)
      WRITE(63,rec=1) (KBUF(i),i=1,ITOT)



C...FORMAT CARDS
C
  900 FORMAT(1x,I8)
  901 FORMAT(18F8.2)
  902 FORMAT(10F8.2)
  903 FORMAT(10F8.4)
  904 FORMAT(8F9.4)
  905 FORMAT(I4,2I2)
  906 FORMAT(48I1)
  907 FORMAT(10F8.1)
  908 FORMAT(1X,'Earth-Sun Distance Ratio =', f9.6)
  909 FORMAT(I8,1X,I2,1X,I3)
  910 FORMAT(' YYYYMMDD =', I8,/,' CYCL =', I3,/,' FCST HR =', I3,
     > /,'1st HOUR OF 6=',I3,/,'2nd Hour of 6=',i3)
  911 FORMAT(18f5.0)
  912 FORMAT(1X,'MXSIZE=',I8)
  913 FORMAT(1X,'ERY5 max =',F8.2,' ERY min =',F8.2)
  919 FORMAT(1X,'YYYY =', I5,', MM =', I3,', DD =', I3,', DOY =', I4)
  920 FORMAT(1X,'DOY =', I4, ' CYCL=', I4,' FCST HR=', I4,' UTC=', I4)
  921 FORMAT(1X,'SOLDEC (DEG)=', F8.2)
  922 FORMAT(1X,'GLON =', F8.5)
  923 FORMAT(1X,'IL=', i4,' RLAT=',F8.2,' MAX SZA =', F8.3,
     > ' MIN SZA =', F8.3)
  924 FORMAT(1X,'IL=', i4,' RLAT=',F8.2,' MAX ERY1 =', F8.3,
     > ' MIN ERY1 =', F8.3)
  925 FORMAT(1X,'IL=', i4,' RLAT=',F8.2,' MAX ERY2 =', F8.3,
     > ' MIN ERY2 =', F8.3)
  926 FORMAT(1X,'IL=', i4,' RLAT=',F8.2,' MAX ERY3 =', F8.3,
     > ' MIN ERY3 =', F8.3)
  927 FORMAT(1X,'IL=', i4,' RLAT=',F8.2,' MAX ERY4 =', F8.3,
     > ' MIN ERY4 =', F8.3, ' N=',I4,' NN =', I4)
  928 FORMAT(1X,'IL=', i4,' RLAT=',F8.2,' MAX ERY5 =', F8.3,
     > ' MIN ERY5 =', F8.3)
  940 FORMAT(1X,'LAT=',F6.2,' OZONE=',F8.2,' OZ =', F6.2,' SZA =', 
     > F8.2,' SZ =', F6.2, ' EOUT=',F8.2)
  945 FORMAT(1x,'LAT=', F7.2,' SZA=', F6.2,' OZONE=', F7.2,
     > ' ALBEDO=', F6.2,' UV CLR=', F5.2,' UV CLD=', F5.2)
  950 FORMAT(2I4,4F8.4)
C
      END