Skip to content
Snippets Groups Projects
sunGeomtry.f90 9.41 KiB
! SUMMA - Structure for Unifying Multiple Modeling Alternatives
! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington
!
! This file is part of SUMMA
!
! For more information see: http://www.ral.ucar.edu/projects/summa
!
! This program 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.
!
! This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

MODULE sunGeomtry_module
USE nrtype
implicit none
private
public::clrsky_rad
contains

! *************************************************************************************************
! public subroutine CLRSKY_RAD: get hourly radiation index
! *************************************************************************************************
 SUBROUTINE CLRSKY_RAD(MONTH,DAY,HOUR,DT,SLOPE,AZI,LAT,HRI,COSZEN)
 ! ----------------------------------------------------------------------------------------
 ! Used to get hourly radiation index
 !
 ! Modification history
 !  - comments added by David Rupp 2006.
 !
 ! Procedure appears similar to Stull(1988) "An Introduction to Boundary Layer
 ! Meteorology " as seen in matlab routine obtained from Joe Kidston <joekidston@yahoo.co.uk>.
 ! Note that equation of time is not used.  Also, solar declination is assumed to stay
 ! constant over the period of one day. What other assumptions are made?  Is this
 ! adequate for the level of precision we require?  Worth reading Stull(1988).
 !
 !  - Modified to integrate over time step up to, but not greater than, 24 hours (D. Rupp, July 2006)
 !
 ! ----------------------------------------------------------------------------------------
 IMPLICIT NONE
 ! Input variables
 INTEGER(I4B), INTENT(IN)                  :: MONTH   ! month as mm integer
 INTEGER(I4B), INTENT(IN)                  :: DAY     ! day of month as dd integer
 REAL(DP), INTENT(IN)                      :: HOUR    ! hour of day as real
 REAL(DP), INTENT(IN)                      :: DT      ! time step in units of hours
 REAL(DP), INTENT(IN)                      :: SLOPE   ! slope of ground surface in degrees
 REAL(DP), INTENT(IN)                      :: AZI     ! aspect (azimuth) of ground surface in degrees
 REAL(DP), INTENT(IN)                      :: LAT     ! latitude in degrees (negative for southern hemisphere)
 ! Outputs
 REAL(DP), INTENT(OUT)                     :: HRI     ! average radiation index over time step DT
 REAL(DP), INTENT(OUT)                     :: COSZEN  ! average cosine of the zenith angle over time step DT
 ! Internal
 REAL(DP)                                  :: CRAD    ! conversion from degrees to radians
 REAL(DP)                                  :: YRAD    ! conversion from year to radians
 REAL(DP)                                  :: T       ! time from noon in radians
 REAL(DP)                                  :: DELT1   ! time step in radians
 REAL(DP)                                  :: SLOPE1  ! slope of ground surface in radians
 REAL(DP)                                  :: AZI1    ! aspect (azimuth) of ground surface in radians
 REAL(DP)                                  :: LAT1    ! latitude in radians
 REAL(DP)                                  :: FJULIAN ! julian date as real
 REAL(DP)                                  :: D       ! solar declination
 REAL(DP)                                  :: LP      ! latitude adjusted for non-level surface (= LAT1 for level surface)
 REAL(DP)                                  :: TD      ! used to calculate sunrise/set
 REAL(DP)                                  :: TPI     ! used to calculate sunrise/set
 REAL(DP)                                  :: TP      ! used to calculate sunrise/set
 REAL(DP)                                  :: DDT     ! used to calculate sunrise/set(= 0 for level surface)
 REAL(DP)                                  :: T1      ! first time in time step or sunrise
 REAL(DP)                                  :: T2      ! last time in time step or sunset
 REAL(DP)                                  :: AUX     ! Auxiliary variable used to check whether the sunset/sunrise time calculation can succeed
 ! ----------------------------------------------------------------------------------------
 ! CONVERSION FACTORS
 !   degrees to radians
 CRAD=PI_D/180.0D0
 !   days-of-year to radians
 YRAD=2.0D0*PI_D/365.0D0
 ! CONVERT TIME TO RADIANS FROM NOON
 T=(HOUR-12.0)*PI_D/12.0D0
 ! Convert time step to radians
 DELT1=DT*PI_D/12.0D0
 ! CONVERT ground slope, ground aspect, and latitude TO RADIANS
 SLOPE1=SLOPE*CRAD  ! tilt angle
 AZI1=AZI*CRAD ! surface-solar Azimuth ??
 LAT1=LAT*CRAD ! latitude
 ! Calculate julian date
 FJULIAN=dble(JULIAN(MONTH,DAY))
 ! Calculate solar declination
 D=CRAD*23.5*SIN((FJULIAN-82.0)*YRAD)
 ! Calculate latitude "adjustment" for ground slope, aspect and latitude (LP = LAT1 for level surface)
 LP=ASIN(SIN(SLOPE1)*COS(AZI1)*COS(LAT1) + COS(SLOPE1)*SIN(LAT1)) ! angle between solar rays and surface (tilted) ??
 ! Calculate time of sunrise/sunset on level surface as radians from noon
 ! Account for high latitude locations, where there might not be a sunrise/sunset time on a given day
 ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases, to fix sunrise at 00.00 or 24.00 of the current day (instead of some time before/after the current day)
 AUX=-TAN(LAT1)*TAN(D)
 IF(abs(AUX) > 1.) THEN
  TD=ACOS(SIGN(1._dp, AUX))
 ELSE
  TD=ACOS(AUX)
 END IF
 ! print *, 'Sunrise = ', TD
 ! Calculate time of sunrise/sunset adjusted for inclined ground surface as radians from noon???
 TPI=-TAN(LP)*TAN(D)
 IF(ABS(TPI).LT.1.0) THEN
  TP=ACOS(TPI)
 ELSE
  TP=0.0
 ENDIF
 ! Calculate time adjustment for ground slope, aspect and latitude (DDT = 0 for level surface)
 DDT=ATAN(SIN(AZI1)*SIN(SLOPE1)/(COS(SLOPE1)*COS(LAT1)-COS(AZI1)*SIN(SLOPE1)*SIN(LAT1)))
 ! print*, 'ddt = ', ddt
 ! Set beginning time of time step (set to sunrise if before sunrise)
 T1=MAX(T,-TP-DDT,-TD)
 ! Set end time of time step (adjust if after sunset)
 T2=MIN(T+DELT1,TD,TP-DDT)
 ! print *, 'First t1 and t2 = ', t1, t2
 IF(T2.LE.T1) THEN
  HRI=0.0 ! nighttime
 ELSE
 ! Calculate integral of radiation index from T1 to T2 and divide by time step DELTA1
 ! NOTE: this assumes the declination does not change from T1 to T2
  HRI=(SIN(D)*SIN(LP)*(T2-T1)+COS(D)*COS(LP)*(SIN(T2+DDT) &
      -SIN(T1+DDT)))/(COS(SLOPE1)*DELT1)  ! radiation index
 ENDIF
 ! print *, hri
 ! ----------------- for time intervals that extend to following day ----------------------
 ! Check to see of timestep extends to following day
 IF((T+DELT1).GT.PI_D) THEN
  ! Advance julian day by 1
  FJULIAN = FJULIAN + 1
  ! Calculate solar declination
  D=CRAD*23.5*SIN((FJULIAN-82.0)*YRAD)
  ! Calculate time of sunrise/sunset on level surface as radians from noon
  ! Account for high latitude locations, where there might not be a sunrise/sunset time on a given day
  ! In such cases AUX > 1 or AUX < -1. Fix AUX at (-)1 in those cases
  AUX=-TAN(LAT1)*TAN(D)
  IF(abs(AUX) > 1.) THEN
   TD=ACOS(SIGN(1._dp, AUX))
  ELSE
   TD=ACOS(AUX)
  END IF

  ! print *, 'Sunrise #2 = ', TD, DELT1
  ! Calculate time of sunrise/sunset adjusted for inclined ground surface as radians from noon???
  TPI=-TAN(LP)*TAN(D)
  IF(ABS(TPI).LT.1.0) THEN
   TP=ACOS(TPI)
  ELSE
   TP=0.0
  ENDIF
  ! Set beginning time to sunrise
  T1=MAX(-TP-DDT,-TD)
  ! Set end time of time step
  T2=MIN(T+DELT1-2*PI_D,TD,TP-DDT)
  ! print *, 'Second t1 and t2 = ', t1, t2
  IF(T2.LE.T1) THEN
   HRI=HRI ! still nighttime in day 2
  ELSE
   ! Calculate integral of radiation index from T1 to T2 and divide by time step DELTA1
   ! NOTE: this assumes the declination does not change from T1 to T2
   HRI=HRI+(SIN(D)*SIN(LP)*(T2-T1)+COS(D)*COS(LP)*(SIN(T2+DDT) &
           -SIN(T1+DDT)))/(COS(SLOPE1)*DELT1)  ! radiation index
  ENDIF
  ! print *, hri
 ENDIF
 ! ----------------------------------------------------------------------------------------
 ! Calculate cosine of solar zenith angle (= HRI for level surface)
 COSZEN = HRI*COS(SLOPE1)
 ! this is assumed to be an appropriate representative value over the
 ! time step.  It is used for albedo calculations.
 ! ----------------------------------------------------------------------------------------
 CONTAINS


 ! *************************************************************************************************
 ! internal function JULIAN: calculate day of year
 ! *************************************************************************************************
  FUNCTION JULIAN(MONTH,DAY)
  USE nrtype
  IMPLICIT NONE
  ! input
  INTEGER(I4B)                             :: MONTH,DAY  ! month and day
  ! output
  INTEGER(I4B)                             :: JULIAN     ! julian day
  ! internal
  INTEGER(I4B),DIMENSION(12)               :: MADD       ! julian day at start of each month
  ! specify the julian day at the start of each month (-1)
  MADD = (/0,31,59,90,120,151,181,212,243,273,304,334/)
  ! compute the julian day
  JULIAN=DAY+MADD(MONTH)
  RETURN
  END FUNCTION JULIAN
 ! ----------------------------------------------------------------------------------------
 END SUBROUTINE CLRSKY_RAD


end module sunGeomtry_module