!! Manage solar radiation with arbitrary time cumulation
!|author: Giovanni Ravazzani
! license: GPL
!
!### History
!
! current version 1.1 - 16th June 2021
!
! | version | date | comment |
! |----------|-------------|----------|
! | 1.0 | 19/May/2021 | Original code |
! | 1.1 | 26/Jun/2021 | added net radiation computation |
!
!### License
! license: GNU GPL
!
!### Module Description
! Module to manage solar radiation with arbitrary time cumulation
!
MODULE SolarRadiation
! Modules used:
USE DataTypeSizes, ONLY : &
! Imported Parameters:
float, short
USE Loglib, ONLY : &
!Imported routines:
Catch
USE ObservationalNetworks, ONLY : &
!Imported types:
ObservationalNetwork, &
!Imported routines:
ReadMetadata, ReadData, &
CopyNetwork, &
!Imported operators:
ASSIGNMENT (=)
USE Chronos, ONLY : &
!Imported types:
DateTime, &
!Imported variables:
timeString, &
!imported routines:
DayOfYear, GetHour, &
!Imported operations:
OPERATOR (<), &
OPERATOR (>), &
OPERATOR (>=), &
OPERATOR (<=), &
OPERATOR (+), &
OPERATOR (==), &
ASSIGNMENT (=)
USE GridLib, ONLY : &
!Imported types:
grid_real, grid_integer, &
!Imported routines:
NewGrid, GetDtGrid, &
GridDestroy, ExportGrid, &
SetLongName, SetStandardName, &
SetUnits, SetCurrentTime, &
!Imported parameters:
NET_CDF, ESRI_ASCII, &
ESRI_BINARY
USE GridOperations, ONLY : &
!Imported routines:
GridByIni, GridResample, &
CRSisEqual, GridConvert, &
GetXY, GetIJ, &
!Imported operations:
ASSIGNMENT (=)
USE Units, ONLY : &
!imported parameters:
degToRad, pi, hour, stefanBoltzman
USE IniLib, ONLY : &
!Imported types:
IniList, &
!Imported routines:
IniReadInt, IniReadReal, &
KeyIsPresent, IniReadString
USE Utilities, ONLY: &
!Imported routines:
GetUnit
USE MeteoUtilities, ONLY: &
!Imported routines:
ReadField, Interpolate, &
Offset, Scale
USE FileSys, ONLY: &
!Imported routines:
DirExists, FileExists, &
FileDelete, CurrentDir, &
GetOS, DirNew, &
!Imported parameters:
WIN32
USE Morphology, ONLY: &
!imported routines:
DeriveAspect, DeriveSlope
USE GeoLib, ONLY: &
!imported type
Coordinate, &
!Imported variables:
point1, point2, &
!Imported routines:
DecodeEPSG, Convert, &
Distance, &
!Imported types:
CRS, &
!Imported operations:
OPERATOR (==)
USE DomainProperties, ONLY : &
!imported variables
latCentroid
USE StringManipulation, ONLY: &
!Imported routines:
ReplaceChar, ToString
IMPLICIT NONE
! Global (i.e. public) Declarations:
INTEGER (KIND = short) :: dtRadiation = 0!!cumulation deltat
TYPE (ObservationalNetwork) :: radiometers !!radiation stations network
TYPE (grid_real) :: radiation !![W/m2]
TYPE (grid_real) :: netRadiation !!incoming and outcoming shortwave and longwave radiation [W/m2]
TYPE (grid_real) :: netRadiationFAO !!net radiation of FAO reference grass with albedo = 0.23
!Private declarations
!define type for viewing angle: the maximum angle of terrain obstruction
TYPE ViewingAngle
REAL (KIND = float) :: angle (16)
END TYPE ViewingAngle
TYPE(DateTime), PRIVATE :: timeNew !!time when new data must be read
INTEGER (KIND = short), PRIVATE :: fileunit !! unit of file containing data
INTEGER (KIND = short), PRIVATE :: interpolationMethod !!method to spatial interpolate site data
INTEGER (KIND = short), PRIVATE :: interpolationMethod_assignment !!method to assign spatial interpolation !
!!1 = one method for the entire domain, 2 = a map with interpolation method codes
TYPE (grid_integer), PRIVATE :: interpolationMethod_map
INTEGER (KIND = short), PRIVATE :: interpolationMethod_vector (3) !!defines active interpolation methods
TYPE (grid_real), PRIVATE :: interpolatedMap (3) !!1 = map for thiessen, 2 = map for idw, 3 = map for kriging
REAL (KIND = float), PRIVATE :: cellsizeInterpolation !!spatial resolution of interpolated grid
INTEGER (KIND = short), PRIVATE :: neighbors !!number of closest data to use for interpolation
TYPE(grid_real), PRIVATE :: grid_devst !!standard deviation of kriging interpolation
CHARACTER (LEN = 300), PRIVATE :: fileGrid !!file containing grids
INTEGER (KIND = short), PRIVATE :: dtGrid !!dt of imported field
INTEGER (KIND = short), PRIVATE :: elevationDrift !! 1 = use elevation to modify interpolated data
INTEGER (KIND = short), PRIVATE :: timezone !!local timezone
TYPE(grid_real), PRIVATE :: shadowGrid !!shadow grid 1 = shadow, 0 = no shadow
TYPE (ViewingAngle), ALLOCATABLE, PRIVATE :: viewangle (:,:) !!raster of maximum viewing angles
REAL (KIND = float), PRIVATE :: azimuth (16)
TYPE(grid_real), PRIVATE :: slope !!terreain slope to be used to modify interpolated data [radians]
TYPE(grid_real), PRIVATE :: aspect !!terreain aspect to be used to modify interpolated data [radians]
INTEGER (KIND = short), PRIVATE :: i, j !!loop index
REAL (KIND = float), PRIVATE, PARAMETER :: MISSING_DEF_REAL = -9999.9
REAL (KIND = float), PRIVATE :: scale_factor !!scale factor to apply to final map
REAL (KIND = float), PRIVATE :: offset_value !!offset to apply to final map
REAL (KIND = float), PRIVATE :: valid_prcn !!when data from several time steps are read,
!!this is the minimum percentage (0-1) of valid
!!data that must be prresent to consider
!!valid the aggregated value.
!used by inverse distance weighting interpolation
REAL (KIND = float), PRIVATE :: idw_power !! power to be used with IDW
!used by kriging
INTEGER (KIND = short), PRIVATE :: krige_var !! when set to 1 a grid of kriging variance is generated and exported if export option is active, default to 0
INTEGER (KIND = short), PRIVATE :: krige_anisotropy !! considers anisotropy, default = 0 excludes anisotropy
INTEGER (KIND = short), PRIVATE :: krige_varmodel !! 1 = spherical, 2 = exponential, 3 = gaussian, 0 = automatic fitting. default to 2
INTEGER (KIND = short), PRIVATE :: krige_lags !! number of lags for semivariogram. if undefined or set to 0 default to 15
REAL (KIND = float) , PRIVATE :: krige_maxlag !! maximum distance to be considered for semivariogram assessment. If undefined or set to 0, it is computed automatically
INTEGER (KIND = short), PRIVATE :: export !!activates grid exporting
CHARACTER (LEN = 1000), PRIVATE :: export_path !!folder where to put exported grids
CHARACTER (LEN = 1000), PRIVATE :: export_file !!name of exported file
CHARACTER (LEN = 1000), PRIVATE :: export_file_net !!name of exported net radiation file
CHARACTER (LEN = 1000), PRIVATE :: export_file_var !!name of exported variance grid
TYPE (DateTime), PRIVATE :: export_start !! time and date to start exporting
TYPE (DateTime), PRIVATE :: export_stop !! time and date to stop exporting
INTEGER (KIND = short), PRIVATE :: export_dt !! time between two exportations
TYPE (DateTime), PRIVATE :: timeNewExport !! time when new exporting must occur
INTEGER (KIND = short), PRIVATE :: export_format !! 1 = esri_ascii, 2 = esri_binary, 3 = netcdf
INTEGER (KIND = short), PRIVATE :: export_epsg !! coordinate reference system of exported grid
TYPE (grid_real), PRIVATE :: exportedGrid, exportedGridVar, exportedGridNet
TYPE (CRS), PRIVATE :: exportGridMapping
LOGICAL, PRIVATE :: needConversion
!Public routines
PUBLIC :: SolarRadiationInit
PUBLIC :: SolarRadiationRead
!Local routines
PRIVATE :: SetSpecificProperties
PRIVATE :: SkyView
PRIVATE :: CastShadow
PRIVATE :: SolarDeclination
PRIVATE :: SunElevationAngle
PRIVATE :: SolarHourAngle
PRIVATE :: SolarAzimuth
PRIVATE :: OpticalDepth
PRIVATE :: ComputeNetRadiation
!=======
CONTAINS
!=======
! Define procedures contained in this module.
!==============================================================================
!| Description:
! Initialize solar radiation
SUBROUTINE SolarRadiationInit &
!
( ini, mask, dtMeteo, tstart, dem, dem_loaded, albedo_loaded, &
dtTemperature, dtRelHumidity )
IMPLICIT NONE
TYPE (IniList), INTENT(IN) :: ini
TYPE (grid_integer), INTENT(IN) :: mask !!defines interpolation extent
INTEGER (KIND = short), INTENT(IN) :: dtMeteo !! deltat of meteo data reading
TYPE (DateTime), INTENT(IN) :: tstart !!initial time
TYPE(grid_real), INTENT(in) :: dem !!digital elevation model to be used to modify interpolated data
LOGICAL , INTENT (in) :: dem_loaded !! true if dem has been loaded
LOGICAL , INTENT (in) :: albedo_loaded !! true if dem has been loaded
INTEGER (KIND = short), INTENT(IN) :: dtTemperature !! delta time temperature
INTEGER (KIND = short), INTENT(IN) :: dtRelHumidity !! delta time relative humidity
!local declarations
CHARACTER (LEN = 1000) :: filename
TYPE (grid_real) :: meteoTemp
!-------------------------end of declarations----------------------------------
!check if albedo and dem have been loaded by domain properties
IF ( .NOT. dem_loaded) THEN
CALL Catch ('error', 'SolarRadiation', &
'dem was not loaded ')
END IF
IF ( .NOT. albedo_loaded) THEN
CALL Catch ('error', 'SolarRadiation', &
'albedo was not loaded ')
END IF
!check if air temperature and relative humidity have been initialized
IF ( dtTemperature <= 0) THEN
CALL Catch ('error', 'SolarRadiation', &
'air temperature not initialized ')
END IF
IF ( dtRelHumidity <= 0) THEN
CALL Catch ('error', 'SolarRadiation', &
'air relative humidity not initialized ')
END IF
!set initial time
timeNew = tstart
!initialize grid
CALL NewGrid (radiation, mask, 0.)
CALL NewGrid (grid_devst, mask, 0.)
!set deltat
dtRadiation = IniReadInt ('dt', ini, section = 'solar-radiation')
!check dt is multiple of dtMeteo
IF (.NOT.(MOD(dtRadiation,dtMeteo) == 0)) THEN
CALL Catch ('error', 'SolarRadiation', &
'dt is not multiple of dtMeteo')
END IF
!set valid threshold
IF (KeyIsPresent ('valid-threshold', ini, &
section = 'solar-radiation') ) THEN
valid_prcn = IniReadReal ('valid-threshold', ini, &
section = 'solar-radiation')
ELSE ! set to default = 1.0
CALL Catch ('info', 'SolarRadiation', &
'valid-threshold not defined, set to 1')
valid_prcn = 1.
END IF
!set cell-size
cellsizeInterpolation = mask % cellsize
!interpolation-assignment method
IF (KeyIsPresent('interpolation-assignment', ini, &
section = 'solar-radiation') ) THEN
interpolationMethod_assignment = IniReadInt ('interpolation-assignment', &
ini, section = 'solar-radiation')
ELSE
CALL Catch ('error', 'SolarRadiation', &
'interpolation-assignment missing in meteo configuration file')
END IF
!set interpolation method
IF (interpolationMethod_assignment == 1) THEN
interpolationMethod = IniReadInt ('interpolation', ini, &
section = 'solar-radiation')
CALL SetSpecificProperties (interpolationMethod, ini, mask)
ELSE !read map
interpolationMethod = -1
CALL GridByIni (ini, interpolationMethod_map, 'solar-radiation', &
"interpolation")
!scan for interpolation methods included
interpolationMethod_vector = 0
DO i = 1, interpolationMethod_map % idim
DO j = 1, interpolationMethod_map % jdim
IF (interpolationMethod_map % mat(i,j) /= &
interpolationMethod_map % nodata) THEN
interpolationMethod_vector &
(interpolationMethod_map % mat (i,j)) = &
interpolationMethod_map % mat (i,j)
END IF
END DO
END DO
DO i = 1, 3
CALL SetSpecificProperties (interpolationMethod_vector (i), ini, mask)
END DO
END IF
!allocate variable for netradiation
CALL NewGrid (netRadiation, mask)
CALL NewGrid (netRadiationFAO, mask)
!scale factor and offset
IF (KeyIsPresent('offset', ini, section = 'solar-radiation')) THEN
offset_value = IniReadReal ('offset', ini, section = 'solar-radiation')
ELSE
offset_value = MISSING_DEF_REAL
END IF
IF (KeyIsPresent('scale-factor', ini, section = 'solar-radiation')) THEN
scale_factor = IniReadReal ('scale-factor', ini, section = 'solar-radiation')
ELSE
scale_factor = MISSING_DEF_REAL
END IF
!set power_idw
IF (KeyIsPresent('idw-power', ini, section = 'solar-radiation')) THEN
idw_power = IniReadReal ('idw-power', ini, section = 'solar-radiation')
ELSE !set default value
idw_power = 2.
END IF
!file
filename = IniReadString ('file', ini, section = 'solar-radiation')
IF (interpolationMethod_assignment == 1 .AND. &
interpolationMethod == 0) THEN !data are stored in net-cdf file
!store net-cdf filename
fileGrid = filename
IF ( KeyIsPresent ('variable', ini, section = 'solar-radiation') ) THEN
radiation % var_name = IniReadString ('variable', &
ini, section = 'solar-radiation')
!read grid and store as temporary file
CALL NewGrid (layer = meteoTemp, fileName = TRIM(fileGrid), &
fileFormat = NET_CDF, &
variable = TRIM(radiation % var_name) )
ELSE IF (KeyIsPresent ('standard_name', ini, &
section = 'solar-radiation') ) THEN
radiation % standard_name = IniReadString ('standard_name', &
ini, section = 'solar-radiation')
ELSE
CALL Catch ('error', 'SolarRadiation', &
'standard_name or variable missing in section temeprature' )
END IF
!set cellsize to zero. Optimal cellsize is automatically computed
cellsizeInterpolation = 0.
ELSE !open file containing local measurements
fileunit = GetUnit ()
OPEN(fileunit, file = filename(1:LEN_TRIM(filename)),status='old')
END IF
!use elevation to drift interpolation
IF ( KeyIsPresent ('elevation-drift', ini, section = 'solar-radiation') ) THEN
elevationDrift = IniReadInt ('elevation-drift', ini, section = &
'solar-radiation')
ELSE
elevationDrift = 0 !default, suppress drift
END IF
IF (elevationDrift == 1) THEN
IF (interpolationMethod == 0 ) THEN
CALL Catch ('error', 'SolarRadiation', &
'elevation drift cannot be applied when interpolation = 0 ')
END IF
IF ( KeyIsPresent ('time-zone', ini, section = 'solar-radiation') ) THEN
timezone = IniReadInt ('time-zone', ini, section = &
'solar-radiation')
ELSE
CALL Catch ('error', 'SolarRadiation', &
'timezone is missing' )
END IF
!set azimuth angles to compute shadow and convert to radians
azimuth = (/0.0, 22.5, 45.0, 67.5, 90.0, 112.5, 135.0, 157.5, &
180.0, 202.5, 225.0, 247.5, 270.0, 292.5, 315.0, 337.5/)
azimuth = azimuth * degToRad
CALL DeriveSlope (dem, slope)
CALL DeriveAspect (dem, aspect)
CALL NewGrid (shadowGrid, dem)
ALLOCATE ( viewangle (dem % idim,dem % jdim) )
END IF
!grid exporting settings
IF (KeyIsPresent ('export', ini, section = 'solar-radiation') ) THEN
export = IniReadInt ('export', ini, section = 'solar-radiation')
ELSE
export = 0
END IF
IF (export == 1) THEN
!set export path name
IF (KeyIsPresent ('export-path', ini, section = 'solar-radiation') ) THEN
export_path = IniReadString ('export-path', ini, section = 'solar-radiation')
!check if path ends with / or \
IF ( export_path (LEN_TRIM (export_path):LEN_TRIM (export_path)) /= '\' .AND. &
export_path (LEN_TRIM (export_path):LEN_TRIM (export_path)) /= '/' ) THEN
export_path (LEN_TRIM (export_path)+1:LEN_TRIM (export_path)+1) = '\'
END IF
IF (INDEX (export_path,'.') == 1) THEN !detected relative path, remove '.'
export_path = export_path (2:LEN_TRIM(export_path))
!build absolute path
export_path = TRIM(CurrentDir() ) // TRIM(export_path)
END IF
!check OS convention
IF (GetOS () == WIN32) THEN
export_path = ReplaceChar (export_path,'/','\')
ELSE
export_path = ReplaceChar (export_path,'\','/')
END IF
!check folder exists
IF ( .NOT. DirExists (TRIM (export_path) ) ) THEN
CALL Catch ('info', 'SolarRadiation', &
'creating directory: ', argument = TRIM(export_path))
CALL DirNew (export_path)
END IF
ELSE
CALL Catch ('error', 'SolarRadiation', &
'missing export-path')
END IF
!starting time
IF (KeyIsPresent ('export-start', ini, section = 'solar-radiation') ) THEN
timeString = IniReadString ('export-start', ini, 'solar-radiation')
export_start = timeString
ELSE
CALL Catch ('error', 'SolarRadiation', &
'missing export-start')
END IF
!initialize timeNewExport
timeNewExport = export_start
!ending time
IF (KeyIsPresent ('export-stop', ini, section = 'solar-radiation') ) THEN
timeString = IniReadString ('export-stop', ini, 'solar-radiation')
export_stop = timeString
ELSE
CALL Catch ('error', 'SolarRadiation', &
'missing export-start')
END IF
!export dt
IF (KeyIsPresent ('export-dt', ini, section = 'solar-radiation') ) THEN
export_dt = IniReadInt ('export-dt', ini, section = 'solar-radiation')
ELSE
CALL Catch ('error', 'SolarRadiation', &
'missing export-dt')
END IF
!coordinate reference system
IF (KeyIsPresent ('export-epsg', ini, section = 'solar-radiation') ) THEN
export_epsg = IniReadInt ('export-epsg', ini, section = 'solar-radiation')
exportGridMapping = DecodeEPSG (export_epsg)
IF (exportGridMapping == radiation % grid_mapping) THEN
needConversion = .FALSE.
!initialize grid for exporting with CRS as meteo variable
CALL NewGrid (exportedGrid, radiation)
CALL NewGrid (exportedGridVar, radiation)
CALL NewGrid (exportedGridNet, netRadiation)
ELSE
needConversion = .TRUE.
!initialize grid for converting with required CRS
exportedGrid % grid_mapping = DecodeEPSG (export_epsg)
exportedGridVar % grid_mapping = DecodeEPSG (export_epsg)
exportedGridNet % grid_mapping = DecodeEPSG (export_epsg)
END IF
ELSE
CALL Catch ('info', 'SolarRadiation', &
'export-epsg not defined, use default')
needConversion = .FALSE.
!initialize grid for exporting with CRS
CALL NewGrid (exportedGrid, radiation)
CALL NewGrid (exportedGridVar, radiation)
CALL NewGrid (exportedGridNet, netRadiation)
END IF
!export file format
IF (KeyIsPresent ('export-format', ini, section = 'solar-radiation') ) THEN
export_format = IniReadInt ('export-format', ini, section = 'solar-radiation')
ELSE
CALL Catch ('error', 'SolarRadiation', &
'missing export-format')
END IF
IF (export_format == 3) THEN
!grid map
CALL SetLongName ( 'downwelling_shortwave_flux_in_air', exportedGrid)
CALL SetLongName ( 'net_downward_flux_in_air', exportedGridNet)
CALL SetStandardName ( 'downwelling_shortwave_flux_in_air', exportedGrid)
CALL SetStandardName ( 'net_downward_flux_in_air', exportedGridNet)
CALL SetUnits ('Wm-2', exportedGrid)
CALL SetUnits ('Wm-2', exportedGridNet)
!if file exists, remove it
export_file = TRIM(export_path) // 'radiation.nc'
IF ( FileExists (export_file) ) THEN
CALL FileDelete (export_file)
END IF
export_file_net = TRIM(export_path) // 'net_radiation.nc'
IF ( FileExists (export_file_net) ) THEN
CALL FileDelete (export_file_net)
END IF
!variance of kriging
CALL SetLongName ( 'downwelling_shortwave_flux_in_air_variance', exportedGridVar)
CALL SetStandardName ( 'downwelling_shortwave_flux_in_air_variance', exportedGridVar)
CALL SetUnits ('Wm-2', exportedGridVar) !this unit is for exporting grid
!if file exists, remove it
export_file_var = TRIM(export_path) // 'radiation_variance.nc'
IF ( FileExists (export_file_var) ) THEN
CALL FileDelete (export_file_var)
END IF
END IF
END IF
!complete initialization
IF (interpolationMethod == 0) THEN
!Get the dt of imported field. Assume dt is regular
dtGrid = GetDtGrid (filename = fileGrid, checkRegular = .TRUE.)
!check dt is multiple of dtGrid
IF (.NOT.(MOD(dtRadiation,dtGrid) == 0)) THEN
CALL Catch ('error', 'SolarRadiation', &
'dt solar radiation is not multiple of dt of input grid')
END IF
ELSE
!populate metadata
CALL ReadMetadata (fileunit, radiometers)
!check spatial reference system
IF ( .NOT. radiometers % mapping == mask % grid_mapping) THEN
CALL Catch ('info', 'SolarRadiation', &
'converting coordinate of stations')
!convert stations' coordinate
point1 % system = radiometers % mapping
point2 % system = mask % grid_mapping
radiometers % mapping = mask % grid_mapping
DO i = 1, radiometers % countObs
point1 % easting = radiometers % obs (i) % xyz % easting
point1 % northing = radiometers % obs (i) % xyz % northing
point1 % elevation = radiometers % obs (i) % z
CALL Convert (point1, point2)
radiometers % obs (i) % xyz % easting = point2 % easting
radiometers % obs (i) % xyz % northing = point2 % northing
END DO
END IF
END IF !interpolationMethod
RETURN
END SUBROUTINE SolarRadiationInit
!==============================================================================
!| Description:
! Read radiation data
!
! References:
!
! Ranzi R., Rosso R., Un modello idrologico distribuito, su base
! fisica, dello scioglimento nivale, Master thesis (in italian),
! Politecnico di Milano,1989.
!
SUBROUTINE SolarRadiationRead &
!
( time, dem, albedo, temp, relHum )
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(IN) :: time !!current time
TYPE (grid_real), INTENT(IN) :: dem !!used to apply drift of station data
TYPE (grid_real), INTENT(IN) :: albedo !!used to apply drift of radiation site data
TYPE (grid_real), INTENT(IN) :: temp !! air temperature (degree celsius)
TYPE (grid_real), INTENT(IN) :: relHum !! air relative humidity (0-100)
!local declarations:
TYPE (DateTime) :: time_toread !! time to start reading from
TYPE (DateTime) :: timeLocal !! local time for applying topographic drift
CHARACTER (LEN = 300) :: filename
CHARACTER (LEN = 300) :: varname
REAL (KIND = float) :: rsquare
INTEGER (KIND = short) :: i, j
REAL (KIND = float) :: d !!solar declination
REAL (KIND = float) :: w !!solar hour angle
REAL (KIND = float) :: a1 !!sun elevation angle
REAL (KIND = float) :: az !!azimuth
REAL (KIND = float) :: mh !!atmosphere optical depth
REAL (KIND = float) :: z !!terrain elevation
REAL (KIND = float) :: Ic !!clear sky direct radiation
REAL (KIND = float) :: D1 !!clear sky diffuse radiation
REAL (KIND = float) :: DF !!diffuse radiation from surrounding elements
REAL (KIND = float) :: A !!reflexed radiation from surrounding elements
REAL (KIND = float) :: Rstar !! clear sky radiation = Ic + D1
REAL (KIND = float) :: radMeas !!measured value of radiation [W/m2]
REAL (KIND = float) :: kt !! clearness index, cloudiness index complement
REAL (KIND = float) :: fB1 !! hillslope coefficient
REAL (KIND = float) :: cosT !! topographic factor
REAL (KIND = float) :: Q !!direct radiation component on inclined surface
REAL (KIND = float) :: radG !! ground radiation
REAL (KIND = float), PARAMETER :: Isc = 1367. !! solar constant
REAL (KIND = float), PARAMETER :: perclo = 0.22 !!minimum fraction of diffuse radiation
REAL (KIND = float), PARAMETER :: k1 = 0.4 !!atmosphere diffusion coefficient
!-------------------------end of declarations----------------------------------
IF ( .NOT. (time < timeNew) ) THEN
!time_toread = time + - (dtRadiation - radiometers % timeIncrement)
time_toread = time
timeString = time_toread
CALL Catch ('info', 'SolarRadiation', &
'read new solar radiation data: ', &
argument = timeString)
SELECT CASE (interpolationMethod)
CASE (0) !input grid
CALL ReadField (fileGrid, time_toread, &
dtRadiation, dtGrid, &
'M', radiation, &
varName = radiation % var_name)
CASE DEFAULT !requires interpolation
!read new data
CALL ReadData (network = radiometers, fileunit = fileunit, &
time = time_toread, aggr_time = dtRadiation, &
aggr_type = 'ave', tresh = valid_prcn)
IF (interpolationMethod_assignment == 1) THEN !only one method is applied
CALL Interpolate (network = radiometers, &
method = interpolationMethod, &
near = neighbors, &
idw_power = idw_power, &
anisotropy = krige_anisotropy, &
varmodel = krige_varmodel, &
lags = krige_lags, &
maxlag = krige_maxlag, &
grid = radiation, &
devst = grid_devst)
ELSE
!loop trough interpolation methods
DO i = 1, 3
IF (interpolationMethod_vector(i) > 0) THEN
CALL Interpolate (network = radiometers, &
method = interpolationMethod_vector(i), &
near = neighbors, &
idw_power = idw_power, &
anisotropy = krige_anisotropy, &
varmodel = krige_varmodel, &
lags = krige_lags, &
maxlag = krige_maxlag, &
grid = interpolatedMap (i), &
devst = grid_devst)
END IF
END DO
!compose the final interpolated field
DO i = 1, interpolationMethod_map % idim
DO j = 1, interpolationMethod_map % jdim
IF (interpolationMethod_map % mat(i,j) /= &
interpolationMethod_map % nodata ) THEN
radiation % mat (i,j) = &
interpolatedMap (interpolationMethod_map % mat(i,j)) % mat(i,j)
END IF
END DO
END DO
END IF !1 or more interpolation methods
END SELECT
IF (elevationDrift == 1) THEN !adjust downwelling shortwave
!radiation to account for topography
timeLocal = time + INT (timezone * hour)
!compute solar declination
d = SolarDeclination (timeLocal)
!compute solar hour angle
w = SolarHourAngle (timeLocal)
!compute sun elevation angle
a1 = SunElevationAngle (timeLocal, latCentroid)
!compute azimuth
az = SolarAzimuth (timeLocal, latCentroid)
!compute shadow map
CALL CastShadow (az, a1, viewangle, shadowGrid)
DO i = 1, radiation % idim
DO j = 1, radiation % jdim
IF (radiation % mat (i,j) /= radiation % nodata ) THEN
radMeas = radiation % mat (i,j)
IF ( a1 <= 0 ) THEN !sun is below horizon
radiation % mat (i,j) = radMeas
netRadiation % mat (i,j) = radMeas
netRadiationFAO % mat (i,j) = radMeas
CYCLE
END IF
!elevation
z = dem % mat (i,j)
!optical depth
mh = OpticalDepth (timeLocal, latCentroid, z)
!clear sky direct radiation
Ic = Isc * EXP ( -mh / SIN (a1) ) * SIN (a1)
!clear sky diffuse radiation
D1 = k1 * ( Isc * SIN (a1) - Ic )
!total clear sky radiation
Rstar = Ic + D1
!clearness index, cloudiness factor complement (Ranzi, 1989)
! kt = 0 fully cloudy, kt = 1 clear sky
kt = ( Rstar - radMeas ) / ( ( 1 - perclo ) * Rstar )
IF ( kt < 0. ) THEN
kt = 0.
END IF
IF ( kt > 1. ) THEN
kt = 1.
END IF
!diffuse radiation from surrounding elements
IF ( Ic < 0. ) THEN
DF = 0.
ELSE
DF = MIN (radMeas, D1 * ( 1 - Kt ) + radMeas * Kt)
END IF
!reflexed radiation from surrounding elements
fB1 = 1. - slope % mat (i,j)
A = radMeas * albedo % mat (i,j) * ( 1. - fB1 )
!direct radiation component
cosT = COS (a1) * SIN (slope % mat(i,j) ) * &
COS ( az - aspect % mat(i,j) ) + &
SIN (a1) * COS (slope % mat(i,j) )
Q = ( radMeas - DF ) * cosT / SIN (a1)
!check shadow
IF ( shadowGrid % mat(i,j) == 1 .OR. Q < 0 ) THEN
radG = DF + A
cosT = -1
!error computing radG
ELSE IF( ( Q + DF + A ) > Isc) THEN
radG = Isc
ELSE
radG = Q + DF + A
END IF
radiation % mat (i,j) = radG
!compute net radiation
CALL ComputeNetRadiation ( kt, albedo % mat (i,j), &
radiation % mat (i,j), temp % mat(i,j), &
relHum % mat (i,j), netRadiation % mat (i,j) )
END IF
END DO
END DO
ELSE !compute only cloudiness factor and net radiation
timeLocal = time + INT (timezone * hour)
!compute sun elevation angle
a1 = SunElevationAngle (timeLocal, latCentroid)
DO i = 1, radiation % idim
DO j = 1, radiation % jdim
IF (radiation % mat (i,j) /= radiation % nodata ) THEN
radMeas = radiation % mat (i,j)
IF ( a1 <= 0 ) THEN !sun is below horizon
netRadiation % mat (i,j) = radMeas
netRadiationFAO % mat (i,j) = radMeas
CYCLE
END IF
!elevation
z = dem % mat (i,j)
!optical depth
mh = OpticalDepth (timeLocal, latCentroid, z)
!clear sky direct radiation
Ic = Isc * EXP ( -mh / SIN (a1) ) * SIN (a1)
!clear sky diffuse radiation
D1 = k1 * ( Isc * SIN (a1) - Ic )
!total clear sky radiation
Rstar = Ic + D1
!cloudiness factor complement.
! kt = 0 fully cloudy, kt = 1 clear sky
kt = ( Rstar - radMeas ) / ( ( 1 - perclo ) * Rstar )
IF ( kt < 0. ) THEN
kt = 0.
END IF
IF ( kt > 1. ) THEN
kt = 1.
END IF
!compute net radiation
CALL ComputeNetRadiation ( kt, albedo % mat (i,j), &
radMeas, temp % mat(i,j), relHum % mat (i,j), &
netRadiation % mat (i,j) )
!compute net radiation FAO with albedo = 0.23
CALL ComputeNetRadiation ( kt, 0.23, &
radMeas, temp % mat(i,j), relHum % mat (i,j), &
netRadiationFAO % mat (i,j) )
END IF
END DO
END DO
END IF !elevationDrift
! END SELECT moved up
!apply scale factor and offset, if defined
IF (offset_value /= MISSING_DEF_REAL) THEN
CALL Offset (radiation, offset_value)
CALL Offset (netRadiation, offset_value)
END IF
IF (scale_factor /= MISSING_DEF_REAL) THEN
CALL Scale (radiation, scale_factor)
CALL Scale (netRadiation, scale_factor)
END IF
!grid exporting
IF(export > 0 ) THEN
IF( time == timeNewExport .AND. &
time >= export_start .AND. &
time <= export_stop ) THEN
timeString = time
timeString = timeString(1:19)
timeString(14:14) = '-'
timeString(17:17) = '-'
!radiation
grid_devst % reference_time = radiation % reference_time
IF (needConversion) THEN
CALL GridConvert (radiation, exportedGrid)
CALL GridConvert (grid_devst, exportedGridVar)
ELSE
exportedGrid = radiation
exportedGridVar = grid_devst
END IF
SELECT CASE (export_format)
CASE (1) !esri-ascii
export_file = TRIM(export_path) // TRIM(timeString) // &
'_radiation.asc'
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file))
CALL ExportGrid (exportedGrid, export_file, ESRI_ASCII)
IF (krige_var == 1) THEN !export kriging variance
export_file_var = TRIM(export_path) // TRIM(timeString) // &
'_radiation_variance.asc'
CALL Catch ('info', 'SolarRadiation', &
'exporting variance grid to file: ', &
argument = TRIM(export_file_var))
CALL ExportGrid (exportedGridVar, export_file_var, ESRI_ASCII)
END IF
CASE (2) !esri-binary
export_file = TRIM(export_path) // TRIM(timeString) // &
'_radiation'
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file))
CALL ExportGrid (exportedGrid, export_file, ESRI_BINARY)
IF (krige_var == 1) THEN !export kriging variance
export_file_var = TRIM(export_path) // TRIM(timeString) // &
'_radiation_variance'
CALL Catch ('info', 'SolarRadiation', &
'exporting variance grid to file: ', &
argument = TRIM(export_file_var))
CALL ExportGrid (exportedGridVar, export_file_var, ESRI_BINARY)
END IF
CASE (3) !net_cdf
CALL SetCurrentTime (time, exportedGrid)
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file))
CALL ExportGrid (exportedGrid, export_file, 'radiation', 'append')
IF (krige_var == 1) THEN !export kriging variance
CALL SetCurrentTime (time, exportedGridVar)
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file_var))
CALL ExportGrid (exportedGridVar, export_file_var, &
'radiation_variance', 'append')
END IF
END SELECT
!net radiation
IF (needConversion) THEN
CALL GridConvert (netRadiation, exportedGridNet)
ELSE
exportedGridNet = netRadiation
END IF
SELECT CASE (export_format)
CASE (1) !esri-ascii
export_file = TRIM(export_path) // TRIM(timeString) // &
'_net_radiation.asc'
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file))
CALL ExportGrid (exportedGridNet, export_file, ESRI_ASCII)
CASE (2) !esri-binary
export_file = TRIM(export_path) // TRIM(timeString) // &
'_net_radiation'
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file))
CALL ExportGrid (exportedGridNet, export_file, ESRI_BINARY)
CASE (3) !net_cdf
CALL SetCurrentTime (time, exportedGridNet)
CALL Catch ('info', 'SolarRadiation', &
'exporting grid to file: ', &
argument = TRIM(export_file))
CALL ExportGrid (exportedGridNet, export_file_net, 'net_radiation', 'append')
END SELECT
timeNewExport = timeNewExport + export_dt
END IF
ENDIF
!time forward
timeNew = timeNew + dtRadiation
END IF
RETURN
END SUBROUTINE SolarRadiationRead
!==============================================================================
!| Description:
! set properties and initialize variables for each interpolation method
SUBROUTINE SetSpecificProperties &
!
( method, ini, mask )
IMPLICIT NONE
!Arguments with intent(in):
INTEGER (KIND = short), INTENT(in) :: method
TYPE (IniList) , INTENT(in) :: ini
TYPE (grid_integer) , INTENT(in) :: mask
!----------------------------end of declarations-------------------------------
!set data specific for each interpolation method
SELECT CASE (method)
CASE (1) !thiessen
CALL NewGrid (interpolatedMap (1), mask, 0.)
CASE (2) !inverse distance
CALL NewGrid (interpolatedMap (2), mask, 0.)
!nearest-points
IF (KeyIsPresent ('nearest-points', ini, &
section = 'solar-radiation') ) THEN
neighbors = IniReadInt ('nearest-points', ini, &
section = 'solar-radiation')
ELSE !nearest-points missing
CALL Catch ('error', 'SolarRadiation', &
'nearest-points missing in meteo configuration file')
END IF
CASE (3) !kriging
CALL NewGrid (interpolatedMap (3), mask, 0.)
!nearest-points
IF (KeyIsPresent ('nearest-points', ini, &
section = 'solar-radiation') ) THEN
neighbors = IniReadInt ('nearest-points', ini, &
section = 'solar-radiation')
CALL Catch ('info', 'SolarRadiation', 'neighbors set to: ', &
argument = ToString (neighbors) )
ELSE !nearest-points missing
CALL Catch ('error', 'SolarRadiation', &
'nearest-points missing in meteo configuration file')
END IF
!kriging-variance
IF (KeyIsPresent ('kriging-variance', ini, &
section = 'solar-radiation') ) THEN
krige_var = IniReadInt ('kriging-variance', ini, &
section = 'solar-radiation')
CALL Catch ('info', 'SolarRadiation', &
'kriging variance export set to ', &
argument = ToString(krige_var) )
ELSE !kriging-variance missing
krige_var = 0
CALL Catch ('info', 'SolarRadiation', &
'kriging variance export set to default (0)')
END IF
!kriging-anisotropy
IF (KeyIsPresent ('kriging-anisotropy', ini, &
section = 'solar-radiation') ) THEN
krige_anisotropy = IniReadInt ('kriging-anisotropy', ini, &
section = 'solar-radiation')
CALL Catch ('info', 'SolarRadiation', 'kriging anisotropy set to: ', &
argument = ToString (krige_anisotropy) )
ELSE !kriging-anisotropy missing
krige_anisotropy = 0
CALL Catch ('info', 'SolarRadiation', &
'kriging anisotropy set to default (0)')
END IF
!kriging-variogram-model
IF (KeyIsPresent ('kriging-variogram-model', ini, &
section = 'solar-radiation') ) THEN
krige_varmodel = IniReadInt ('kriging-variogram-model', ini, &
section = 'solar-radiation')
CALL Catch ('info', 'SolarRadiation', 'kriging variogram model set to: ', &
argument = ToString (krige_varmodel) )
IF (krige_varmodel == 0 ) krige_varmodel = 5 !automatic fitting
ELSE !kriging-variogram-model
krige_varmodel = 2
CALL Catch ('info', 'SolarRadiation', &
'kriging variogram model set to default (2 exponential)')
END IF
!kriging-lags
IF (KeyIsPresent ('kriging-lags', ini, &
section = 'solar-radiation') ) THEN
krige_lags = IniReadInt ('kriging-lags', ini, &
section = 'solar-radiation')
IF (krige_lags == 0) krige_lags = 15
CALL Catch ('info', 'SolarRadiation', 'kriging lags set to: ', &
argument = ToString (krige_lags) )
ELSE !kriging-variogram-model
krige_lags = 15
CALL Catch ('info', 'SolarRadiation', &
'kriging lags set to default (15)')
END IF
!kriging-maxlag
IF (KeyIsPresent ('kriging-maxlag', ini, &
section = 'solar-radiation') ) THEN
krige_maxlag = IniReadInt ('kriging-maxlag', ini, &
section = 'solar-radiation')
CALL Catch ('info', 'SolarRadiation', 'kriging max lag set to: ', &
argument = ToString (krige_maxlag) )
ELSE !kriging-maxlag missing
krige_maxlag = 0
CALL Catch ('error', 'SolarRadiation', &
'kriging max lag set to default (0)')
END IF
END SELECT
RETURN
END SUBROUTINE SetSpecificProperties
!==============================================================================
!| Description:
! Compute the maximum angle of sky obstruction along 16 directions
!
! References:
!
! Zhang, Y., Chang, X. & Liang, J. Comparison of different algorithms for
! calculating the shading effects of topography on solar irradiance in
! a mountainous area. Environ Earth Sci 76, 295 (2017).
! https://doi.org/10.1007/s12665-017-6618-5
!
SUBROUTINE SkyView &
!
( dem, azimuth, view)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real) , INTENT(in) :: dem
REAL (KIND = float), INTENT(in) :: azimuth (:) !azimuth angles [rad]
!Arguments with intent(inout):
TYPE (ViewingAngle), INTENT(inout) :: view(:,:)
!local declarations
INTEGER (KIND = short) :: i,j,l,i1,j1
TYPE (Coordinate) :: x0y0, x1y1
REAL (KIND = float) :: delta, deltax, deltay
REAL (KIND = float) :: length
LOGICAL :: check
REAL (KIND = float) :: topAngle, topAngleMax
!----------------------------end of declarations-------------------------------
x0y0 % system = dem % grid_mapping
x1y1 % system = dem % grid_mapping
delta = dem % cellsize / 2.
DO i = 1, dem % idim
DO j = 1, dem % jdim
IF (dem % mat (i,j) /= dem % nodata) THEN
DO l = 1, SIZE (azimuth)
!get coordinate of starting point
CALL GetXY(i, j, dem, x0y0 % easting, x0y0 % northing)
deltax = 0.
deltay = 0.
topAngle = 0.
topAngleMax = 0.
DO
!add increment in x and y
deltax = deltax + delta * SIN (azimuth (l) )
deltay = deltay + delta * COS (azimuth (l) )
x1y1 % easting = x0y0 % easting + deltax
x1y1 % northing = x0y0 % northing + deltay
!retrieve local coordinate of new cell
CALL GetIJ (x1y1 % easting, x1y1 % northing, &
dem, i1, j1, check)
IF (.NOT. check ) THEN
EXIT
ELSE IF (dem % mat (i1, j1) == dem % nodata ) THEN
EXIT
END IF
!compute distance
length = Distance (x1y1, x0y0)
!compute topographic angle
topAngle = ATAN ( (dem % mat (i1, j1) - dem % mat (i,j) ) &
/ length )
IF (topAngle > topAngleMax) topAngleMax = topAngle
END DO
view(i,j) % angle (l) = topAngleMax
END DO
END IF
END DO
END DO
RETURN
END SUBROUTINE SkyView
!==============================================================================
!| Description:
! Compute shadow grid
SUBROUTINE CastShadow &
!
( az, sunHeight, view, grid)
IMPLICIT NONE
!Arguments with intent(in):
REAL (KIND = float), INTENT(in) :: az !current sun azimuth [rad]
REAL (KIND = float), INTENT(in) :: sunHeight !current sun height [rad]
TYPE (ViewingAngle), INTENT(in) :: view (:,:)
!Arguments with intent(inout):
TYPE (grid_real) , INTENT(inout) :: grid
!local declarations
INTEGER (KIND = short) :: i, j, m, l
REAL (KIND = float) :: azDiff, azDiffMin
!----------------------------end of declarations-------------------------------
!search for closer azimuth to current sun azimuth
azDiffMin = 100.
l = 0
DO m = 1, SIZE (azimuth)
azDiff = ABS ( azimuth (m) - az)
IF ( azDiff < azDiffMin) THEN
azDiffMin = azDiff
l = m
END IF
END DO
DO i = 1, grid % idim
DO j = 1, grid % jdim
IF ( grid % mat (i,j) /= grid % nodata ) THEN
IF (sunHeight < view (i,j) % angle (l) ) THEN
grid % mat (i,j) = 1
ELSE
grid % mat (i,j) = 0
END IF
END IF
END DO
END DO
RETURN
END SUBROUTINE CastShadow
!==============================================================================
!| Description:
! Compute solar declination. The declination of the sun is the angle
! between the equator and a line drawn from the centre of the Earth to
! the centre of the sun.
!
! References:
!
! Iqbal, M.: An introduction to solar radiation, Academis Press
! Canada, Ontario, 1983.
FUNCTION SolarDeclination &
!
(time) &
!
RESULT (sdec)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(in) :: time
!local declarations:
REAL (KIND = float) :: sdec ![radians]
REAL (KIND = float) :: dayAngle ![radians]
INTEGER (KIND = short) :: dn !day number of the year
!------------------------------------end of declarations-----------------------
!day of year
dn = DayOfYear (time, 'noleap')
!day angle
dayAngle = 2. * pi * (dn -1) / 365.
!declination
sdec = (0.006918 - 0.399912 * COS (dayAngle) + 0.070257 * SIN (dayAngle) - &
0.006758 * COS (2 * dayAngle) + 0.000907 * SIN (2 * dayAngle) - &
0.002697 * COS (3 * dayAngle) + 0.00148 * SIN (3 * dayAngle))
RETURN
END FUNCTION SolarDeclination
!==============================================================================
!| Description:
! Compute the sun elevation angle
!
! References:
!
! Gates DM (1980) Biophysical ecology. Springer, New York,
! page 101, eq. 6.6
FUNCTION SunElevationAngle &
!
(time, lat) &
!
RESULT (sea)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(in) :: time
REAL (KIND = float), INTENT(in) :: lat !latitude [radians]
!local declarations:
REAL (KIND = float) :: sea ! sun elevation angle [radians]
REAL (KIND = float) :: dec !solar declination [radians]
REAL (KIND = float) :: w !hour angle [radians]
!------------------------------------end of declarations-----------------------
w = SolarHourAngle (time)
dec = SolarDeclination (time)
sea = ASIN ( COS (lat) * COS (dec) * COS (w) + SIN (lat) * SIN (dec) )
RETURN
END FUNCTION SunElevationAngle
!==============================================================================
!| Description:
! Compute the hour angle. the solar hour angle is an expression of time,
! hour angle is 0.000 degree, with the time before solar noon
! expressed as negative degrees, and the local time after solar noon
! expressed as positive degrees. For example, at 10:30 AM local
! apparent time the hour angle is -22.5 degree
! (15 degree per hour times 1.5 hours before noon).
!
! References:
!
! Iqbal, M.: An introduction to solar radiation, Academis Press
! Canada, Ontario, 1983.
FUNCTION SolarHourAngle &
!
(time) &
!
RESULT (w)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(in) :: time
!local declarations:
REAL (KIND = float) :: w ! hour angle [radians]
INTEGER (KIND = short) :: t !hour
!------------------------------------end of declarations-----------------------
t = GetHour (time)
w = 15. * pi / 180. * (t - 12.)
RETURN
END FUNCTION SolarHourAngle
!==============================================================================
!| Description:
! Compute tthe atmospheric optical depth
!
! References:
!
! Kreith, F., and Kreider, J. F., 1978, Principles of Solar Engineering
! (New York: McGraw-Hill ).
!
! Cartwright, T. J., 1993, Modelling the World in a Spreadsheet:
! Environmental Simulation on a Microcomputer
! (Baltimore: Johns Hopkins University Press).
!
! LALIT KUMAR, ANDREW K. SKIDMORE & EDMUND KNOWLES (1997) Modelling
! topographic variation in solar radiation in a GIS environment,
! International Journal of Geographical Information Science,
! 11 :5, 475-497, DOI: 10.1080/136588197242266
FUNCTION OpticalDepth &
!
(time, lat, z) &
!
RESULT (s)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(in) :: time
REAL (KIND = float), INTENT(in) :: lat !latitude [radians]
REAL (KIND = float), INTENT(in) :: z !terrain elevation [m]
!local declarations:
REAL (KIND = float) :: s !!optical depth
REAL (KIND = float) :: presscorr !atmospheric correction factor
REAL (KIND = float) :: s0 !depth at sea level
REAL (KIND = float) :: a1 ! sun elevation angle [radians]
!------------------------------------end of declarations-----------------------
!compute sun elevation angle
a1 = SunElevationAngle (time, lat)
!compute atmospheric optical depth at sea level
s0 = ( 1229. + (614. * SIN (a1) ) **2. )**0.5 -614. * SIN (a1)
!compute pressure correction factor
presscorr = ( ( 288.15 - 0.0065 * z ) / 288.15 ) ** 5.25588
!optical depth
s = s0 * presscorr
RETURN
END FUNCTION OpticalDepth
!==============================================================================
!| Description:
! Compute azimuth angle of the Sun's position in the north-clockwise
! convention
!
! References:
!
! Oke, T.R., Boundary layer climates, Second edition, Routledge, 1987.
! Appendix A1, eq. A1.2
FUNCTION SolarAzimuth &
!
(time, lat) &
!
RESULT (az)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(in) :: time
REAL (KIND = float), INTENT(in) :: lat !latitude [radians]
!local declarations:
REAL (KIND = float) :: az !solar azimuth [radians]
REAL (KIND = float) :: w ! hour angle [radians]
REAL (KIND = float) :: d ! solar declination [radians]
REAL (KIND = float) :: a1 ! sun elevation angle [radians]
INTEGER (KIND = short) :: t !hour
REAL (KIND = float) :: term
!------------------------------------end of declarations-----------------------
!compute hour angle
w = SolarHourAngle (time)
!compute declination
d = SolarDeclination (time)
!compute sun elevation angle
a1 = SunElevationAngle (time, lat)
!get hour
t = GetHour (time)
term = ( SIN (d) * COS (lat) - COS (d) * SIN (lat) * COS (w) ) / COS (a1)
! compute azimuth
IF ( term < -1.) THEN
az = pi
ELSE IF (term > 1.) THEN
az = 0.
ELSE IF (t <= 12) THEN
az = ACOS ( term )
ELSE
az = 2. * pi - ACOS ( term )
END IF
RETURN
END FUNCTION SolarAzimuth
!==============================================================================
!| Description:
! Compute net radiation
!
! References:
!
! Wales-Smith, B. G. (1980). Estimates of net radiation for evaporation
! calculations, Hydrological Sciences Journal, 25:3, 237-242,
! DOI: 10.1080/02626668009491931
!
!
SUBROUTINE ComputeNetRadiation &
!
(cfc, alb, short, hum, temp, netRad)
IMPLICIT NONE
!Arguments with intent(in):
REAL (KIND = float), INTENT(in) :: cfc !!cloudiness factor complement
REAL (KIND = float), INTENT(in) :: alb !!albedo
REAL (KIND = float), INTENT(in) :: short !!shortwave radiation (W/m2)
REAL (KIND = float), INTENT(in) :: hum !! air relative hunidity (0-100)
REAL (KIND = float), INTENT(in) :: temp !! air temperature (degree celsius)
!Arguments with intent(out):
REAL (KIND = float), INTENT(out) :: netRad !!net radiation
!local declarations:
REAL (KIND = float) :: cf !!cloudiness factor
REAL (KIND = float) :: netShort !!net shortwave radiation (W/m2)
REAL (KIND = float) :: netLong !!net longwave radiation (W/m2)
REAL (KIND = float) :: albedotemp !!temporary albedo value
REAL (KIND = float) :: hum01 !!air relative humidity (0-1)
REAL (KIND = float) :: humMin !!minimum value for air relative humidity (0-100)
REAL (KIND = float) :: es !! saturation vapor pressure (Pa)
REAL (KIND = float) :: ea !!actual vapor pressure (Pa)
!------------------------------------end of declarations-----------------------
!cloudiness factor
cf = 1. - cfc
!net shortwave radiation
IF ( alb < 0. ) THEN
albedotemp = 0.2
netShort = (1. - albedotemp) * short
ELSE
netShort = ( 1. - alb ) * short
END IF
!vapor pressure
humMin = 10
IF ( hum > humMin ) THEN
hum01 = hum / 100.
ELSE
hum01 = humMin / 100.
END IF
es = 6.107 * exp ( ( 17.27 * temp ) / ( temp + 237.3 ) )
ea = hum01 * es
!net longwave radiation ( Wales-Smith, 1980)
netLong = - ( stefanBoltzman * (temp + 273.15 )**4. ) * &
( 0.56 - 0.079 * ea**0.5 ) * ( 0.1 + 0.9 * cf )
!total net radiation
netRad = netShort + netLong
RETURN
END SUBROUTINE ComputeNetRadiation
END MODULE SolarRadiation