An error occurred while loading the file. Please try again.
-
Dorchies David authored6458a59c
module similarity_module
use iso_c_binding, only: c_int, c_double
use, intrinsic :: ieee_arithmetic
implicit none
abstract interface
function objective_fun(sim, obs, n) result(val)
use iso_c_binding, only: c_int, c_double
implicit none
integer(c_int), intent(in) :: n
real(c_double), intent(in) :: sim(n), obs(n)
real(c_double) :: val
end function objective_fun
end interface
procedure(objective_fun), pointer :: FUN => null()
contains
subroutine set_objective_function(crit)
implicit none
integer(c_int), intent(in) :: crit
select case (crit)
case(1)
FUN => kge
case(2)
FUN => rmse
case(3)
FUN => invkge
case(4)
FUN => invrmse
case default
FUN => invkge
end select
end subroutine set_objective_function
subroutine similarity(Rn, nt, nb, crit, nthreads, sim_matrix) bind(C, name="similarity")
use, intrinsic :: ieee_arithmetic
use omp_lib
implicit none
integer(c_int), value :: nt, nb, crit, nthreads
real(c_double), intent(in) :: Rn(nt,nb)
real(c_double), intent(out) :: sim_matrix(nb,nb)
integer(c_int) :: i, j
real(c_double) :: val, nan_val
nan_val = ieee_value(0.0_c_double, ieee_quiet_nan)
call set_objective_function(crit)
if (.not. associated(FUN)) then
sim_matrix = nan_val
return
end if
select case (crit)
case(2, 4) ! if symetric, like RMSE
!$omp parallel do num_threads(nthreads) default(shared) private(i, j) schedule(static)
do i = 1, nb
do j = i, nb
sim_matrix(i,j) = FUN(Rn(:,i), Rn(:,j), nt)
sim_matrix(j,i) = sim_matrix(i,j)
end do
end do
!$omp end parallel do
case default ! if not symetric, like KGE
!$omp parallel do num_threads(nthreads) default(shared) private(i, j) schedule(static)
do i = 1, nb
do j = 1, nb
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
sim_matrix(i,j) = FUN(Rn(:,i), Rn(:,j), nt)
end do
end do
!$omp end parallel do
end select
end subroutine similarity
function kge(sim, obs, n) result(kge_val)
use, intrinsic :: ieee_arithmetic
implicit none
integer, intent(in) :: n
real(c_double), dimension(n), intent(in) :: sim, obs
real(c_double) :: kge_val
integer :: i, count
real(c_double) :: sum_obs, sum_sim, sum_obs2, sum_sim2, sum_obs_sim
real(c_double) :: mean_obs, mean_sim, std_obs, std_sim, r, beta, gamma, diff
real(c_double) :: nan_val, eps
eps = 1.0e-12_c_double
count = 0
sum_obs = 0.0_c_double
sum_sim = 0.0_c_double
sum_obs2 = 0.0_c_double
sum_sim2 = 0.0_c_double
sum_obs_sim = 0.0_c_double
nan_val = ieee_value(0.0_c_double, ieee_quiet_nan)
! Loop to filter NaN values and accumulate sums
do i = 1, n
if (.not. ieee_is_nan(obs(i)) .and. .not. ieee_is_nan(sim(i))) then
count = count + 1
sum_obs = sum_obs + obs(i)
sum_sim = sum_sim + sim(i)
sum_obs2 = sum_obs2 + obs(i) * obs(i)
sum_sim2 = sum_sim2 + sim(i) * sim(i)
sum_obs_sim = sum_obs_sim + obs(i) * sim(i)
end if
end do
! Return NA if the common period is not long enough
if (count <= 1) then
kge_val = nan_val
return
end if
! Compute means
mean_obs = sum_obs / count
mean_sim = sum_sim / count
! Check to avoid division by zero
if (abs(mean_obs) < eps) then
kge_val = nan_val
return
end if
! Compute standard deviations (using Welford's correction)
std_obs = sqrt((sum_obs2 - sum_obs * mean_obs) / (count - 1))
std_sim = sqrt((sum_sim2 - sum_sim * mean_sim) / (count - 1))
! Check standard deviations
if (std_obs < eps .or. std_sim < eps) then
kge_val = nan_val
return
end if
! Compute correlation
r = ((sum_obs_sim - count * mean_obs * mean_sim) / (count - 1)) / (std_obs * std_sim)
! Compute KGE components
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
beta = mean_sim / mean_obs
gamma = (std_sim / mean_sim) / (std_obs / mean_obs)
! Final KGE computation
diff = (r - 1.0_c_double)**2 + (beta - 1.0_c_double)**2 + (gamma - 1.0_c_double)**2
kge_val = 1.0_c_double - sqrt(diff)
if (abs(kge_val - 1.0_c_double) < eps) kge_val = 1.0_c_double
end function kge
function rmse(sim, obs, n) result(res)
integer, intent(in) :: n
real(c_double), dimension(n), intent(in) :: sim, obs
real(c_double) :: res
integer :: i, count
real(c_double) :: sum_sq, diff
real(c_double) :: nan_val, eps
nan_val = ieee_value(0.0_c_double, ieee_quiet_nan)
eps = 1.0e-12_c_double
count = 0
sum_sq = 0.0_c_double
do i = 1, n
if (.not. ieee_is_nan(obs(i)) .and. .not. ieee_is_nan(sim(i))) then
count = count + 1
diff = sim(i) - obs(i)
sum_sq = sum_sq + diff**2
end if
end do
if (count == 0) then
res = nan_val
return
end if
res = sqrt(sum_sq / count)
if (res < eps) res = 0.0_c_double
end function rmse
function invrmse(sim, obs, n) result(res)
integer, intent(in) :: n
real(c_double), dimension(n), intent(in) :: sim, obs
real(c_double) :: rmse_val, res
real(c_double) :: inf_val, eps
inf_val = ieee_value(0.0_c_double, ieee_positive_inf)
eps = 1.0e-12_c_double
rmse_val = rmse(obs, sim, n)
if (rmse_val < eps) then
res = inf_val
else
res = 1.0_c_double / rmse_val
end if
end function invrmse
function invkge(sim, obs, n) result(res)
integer, intent(in) :: n
real(c_double), dimension(n), intent(in) :: sim, obs
real(c_double) :: kge_val, res
real(c_double) :: inf_val, eps
inf_val = ieee_value(0.0_c_double, ieee_positive_inf)
eps = 1.0e-12_c_double
kge_val = kge(obs, sim, n)
if (abs(kge_val - 1.0_c_double) < eps) then
res = inf_val
else
res = 1.0_c_double / (1-kge_val)
end if
end function invkge
211212213214
end module similarity_module