LCOV - code coverage report
Current view: top level - mHM - mo_objective_function.F90 (source / functions) Hit Total Coverage
Test: mHM coverage Lines: 262 620 42.3 %
Date: 2024-04-15 17:48:09 Functions: 8 17 47.1 %

          Line data    Source code
       1             : !> \file mo_objective_function.f90
       2             : !> \brief   \copybrief mo_objective_function
       3             : !> \details \copydetails mo_objective_function
       4             : 
       5             : ! ToDo: change comment for OF 15
       6             : 
       7             : !> \brief Objective Functions for Optimization of mHM.
       8             : !> \details This module provides a wrapper for several objective functions used to optimize mHM against various
       9             : !!       variables.
      10             : !!       If the objective is only regarding runoff move it to mRM/mo_mrm_objective_function_runoff.f90.
      11             : !!       If it contains besides runoff another variable like TWS implement it here.
      12             : !!
      13             : !!       All the objective functions are supposed to be minimized!
      14             : !!       - (10) SO: SM:       1.0 - KGE of catchment average soilmoisture
      15             : !!       - (11) SO: SM:       1.0 - Pattern dissimilarity (PD) of spatially distributed soil moisture
      16             : !!       - (12) SO: SM:       Sum of squared errors (SSE) of spatially distributed standard score (normalization) of soil moisture
      17             : !!       - (13) SO: SM:       1.0 - average temporal correlation of spatially distributed soil moisture
      18             : !!       - (15) SO: Q + TWS:  [1.0-KGE(Q)]*RMSE(domain_avg_TWS) - objective function using Q and domain average (standard score) TWS
      19             : !!       - (17) SO: N:        1.0 - KGE of spatio-temporal neutron data, catchment-average
      20             : !!       - (27) SO: ET:       1.0 - KGE of catchment average evapotranspiration
      21             : !> \changelog
      22             : !! - Oldrich Rakovec Oct 2015
      23             : !!   - added obj. func. 15 (objective_kge_q_rmse_tws) and extract_domain_avg_tws routine, former basin_avg
      24             : !! - Robert Schweppe Jun 2018
      25             : !!   - refactoring and reformatting
      26             : !> \authors Juliane Mai
      27             : !> \date Dec 2012
      28             : !> \copyright Copyright 2005-\today, the mHM Developers, Luis Samaniego, Sabine Attinger: All rights reserved.
      29             : !! mHM is released under the LGPLv3+ license \license_note
      30             : !> \ingroup f_mhm
      31             : MODULE mo_objective_function
      32             : 
      33             :   ! This module provides objective functions for optimization of the UFZ CHS mesoscale hydrologic model mHM.
      34             : 
      35             :   ! Written  Juliane Mai, Dec 2012
      36             :   ! Modified Stephan Thober, Oct 2015 moved all runoff only related objectives to mRM
      37             : 
      38             :   USE mo_kind, ONLY : i4, dp
      39             :   use mo_optimization_utils, only : eval_interface
      40             :   use mo_message, only : message, error_message
      41             : 
      42             :   IMPLICIT NONE
      43             : 
      44             :   PRIVATE
      45             : 
      46             :   PUBLIC :: objective
      47             : #ifdef MPI
      48             :   PUBLIC :: objective_master, objective_subprocess ! objective function wrapper for soil moisture only
      49             : #endif
      50             : 
      51             :   ! ------------------------------------------------------------------
      52             : 
      53             : CONTAINS
      54             : 
      55             :   ! ------------------------------------------------------------------
      56             : 
      57             :   !    NAME
      58             :   !        objective
      59             : 
      60             :   !    PURPOSE
      61             :   !>       \brief Wrapper for objective functions.
      62             : 
      63             :   !>       \details The functions selects the objective function case defined in a namelist,
      64             :   !>       i.e. the global variable \e opti\_function.
      65             :   !>       It return the objective function value for a specific parameter set.
      66             : 
      67             :   !    INTENT(IN)
      68             :   !>       \param[in] "REAL(dp), DIMENSION(:) :: parameterset"
      69             :   !>       \param[in] "procedure(eval_interface) :: eval"
      70             : 
      71             :   !    INTENT(IN), OPTIONAL
      72             :   !>       \param[in] "real(dp), optional :: arg1"
      73             : 
      74             :   !    INTENT(OUT), OPTIONAL
      75             :   !>       \param[out] "real(dp), optional :: arg2"
      76             :   !>       \param[out] "real(dp), optional :: arg3"
      77             : 
      78             :   !    RETURN
      79             :   !>       \return real(dp) :: objective — objective function value
      80             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
      81             : 
      82             :   !    HISTORY
      83             :   !>       \authors Juliane Mai
      84             : 
      85             :   !>       \date Dec 2012
      86             : 
      87             :   ! Modifications:
      88             :   ! Stephan Thober Oct 2015 - moved all runoff related objective functions to mRM
      89             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
      90             : 
      91          50 :   FUNCTION objective(parameterset, eval, arg1, arg2, arg3)
      92             : 
      93             :     use mo_common_constants, only : nodata_dp
      94             :     use mo_common_mHM_mRM_variables, only : opti_function
      95             : 
      96             :     implicit none
      97             : 
      98             :     REAL(dp), DIMENSION(:), INTENT(IN) :: parameterset
      99             : 
     100             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     101             : 
     102             :     real(dp), optional, intent(in) :: arg1
     103             : 
     104             :     real(dp), optional, intent(out) :: arg2
     105             : 
     106             :     real(dp), optional, intent(out) :: arg3
     107             : 
     108             :     REAL(dp) :: objective
     109             : 
     110         175 :     real(dp), dimension(6) :: multiple_objective
     111             : 
     112             : 
     113          25 :     if (present(arg1) .or. present(arg2) .or. present(arg3)) then
     114           0 :       call error_message("Error mo_objective_function: Received unexpected argument, check optimization settings")
     115             :     end if
     116             : 
     117             :     ! set these to nan so compiler does not complain
     118           0 :     if (present(arg2)) then
     119           0 :       arg2 = nodata_dp
     120             :     end if
     121          25 :     if (present(arg3)) then
     122           0 :       arg3 = nodata_dp
     123             :     end if
     124             : 
     125           0 :     select case (opti_function)
     126             :     case (10)
     127             :       ! KGE of catchment average SM
     128           0 :       objective = objective_sm_kge_catchment_avg(parameterset, eval)
     129             :     case (11)
     130             :       ! pattern dissimilarity (PD) of SM fields
     131           0 :       objective = objective_sm_pd(parameterset, eval)
     132             :     case (12)
     133             :       ! sum of squared errors of standard_score SM
     134           0 :       objective = objective_sm_sse_standard_score(parameterset, eval)
     135             :     case (13)
     136             :       ! soil moisture correlation - temporal
     137           0 :       objective = objective_sm_corr(parameterset, eval)
     138             :     case (15)
     139             :       ! KGE for Q * RMSE for domain_avg TWS (standarized scored)
     140           6 :       objective = objective_kge_q_rmse_tws(parameterset, eval)
     141             :     case (17)
     142             :       ! KGE of catchment average SM
     143           6 :       objective = objective_neutrons_kge_catchment_avg(parameterset, eval)
     144             :     case (27)
     145             :       ! KGE of catchment average ET
     146           0 :       objective = objective_et_kge_catchment_avg(parameterset, eval)
     147             :     case (28)
     148             :       !  KGE for Q + SSE for SM (standarized scored)
     149           6 :       objective = objective_kge_q_sm_corr(parameterset, eval)
     150             :     case (29)
     151             :       !  KGE for Q + KGE of catchment average ET
     152           0 :       objective = objective_kge_q_et(parameterset, eval)
     153             :     case (30)
     154             :       ! KGE for Q * RMSE for domain_avg ET (standarized scored)
     155           0 :       objective = objective_kge_q_rmse_et(parameterset, eval)
     156             :     case (33)
     157           7 :       multiple_objective = objective_q_et_tws_kge_catchment_avg(parameterset, eval)
     158           7 :       objective = multiple_objective(1)
     159             :     case (34)
     160             :       ! KGE for Q * Absolute-Error for BFI
     161           0 :       objective = objective_kge_q_BFI(parameterset, eval)
     162             : 
     163             :     case default
     164          25 :       call error_message("Error objective: opti_function not implemented yet.")
     165             :     end select
     166             : 
     167          25 :   END FUNCTION objective
     168             : 
     169             :   ! ------------------------------------------------------------------
     170             : 
     171             :   !    NAME
     172             :   !        objective_master
     173             : 
     174             :   !    PURPOSE
     175             :   !>       \brief Wrapper for objective functions.
     176             : 
     177             :   !>       \details The functions sends the parameterset to the subprocess, receives
     178             :   !>       the partial objective and calculates the final objective
     179             :   !    INTENT(IN)
     180             :   !>       \param[in] "REAL(dp), DIMENSION(:) :: parameterset"
     181             :   !>       \param[in] "procedure(eval_interface) :: eval"
     182             : 
     183             :   !    INTENT(IN), OPTIONAL
     184             :   !>       \param[in] "real(dp), optional :: arg1"
     185             : 
     186             :   !    INTENT(OUT), OPTIONAL
     187             :   !>       \param[out] "real(dp), optional :: arg2"
     188             :   !>       \param[out] "real(dp), optional :: arg3"
     189             : 
     190             :   !    RETURN
     191             :   !>       \return real(dp) :: objective — objective function value
     192             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
     193             : 
     194             :   !    HISTORY
     195             :   !>       \authors Juliane Mai
     196             : 
     197             :   !>       \date Dec 2012
     198             : 
     199             :   ! Modifications:
     200             :   ! Stephan Thober Oct 2015 - moved all runoff related objective functions to mRM
     201             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
     202             :   ! Maren Kaluza Jun 2019 - parallel version
     203             : 
     204             : #ifdef MPI
     205             :   FUNCTION objective_master(parameterset, eval, arg1, arg2, arg3)
     206             : 
     207             :     use mo_common_constants, only : nodata_dp
     208             :     use mo_common_mHM_mRM_variables, only : opti_function
     209             :     use mo_common_mpi_tools, only : distribute_parameterset
     210             :     use mo_common_variables, only : domainMeta
     211             :     use mo_string_utils, only : num2str
     212             :     use mpi_f08
     213             : 
     214             :     implicit none
     215             : 
     216             :     REAL(dp), DIMENSION(:), INTENT(IN) :: parameterset
     217             : 
     218             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     219             : 
     220             :     real(dp), optional, intent(in) :: arg1
     221             : 
     222             :     real(dp), optional, intent(out) :: arg2
     223             : 
     224             :     real(dp), optional, intent(out) :: arg3
     225             : 
     226             :     REAL(dp) :: objective_master
     227             : 
     228             :     REAL(dp) :: partial_objective
     229             : 
     230             :     real(dp), dimension(6) :: multiple_partial_objective
     231             : 
     232             :     real(dp), dimension(6) :: multiple_master_objective
     233             : 
     234             :     ! for sixth root
     235             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
     236             : 
     237             :     integer(i4) :: iproc, nproc
     238             : 
     239             :     integer(i4) :: ierror
     240             : 
     241             :     type(MPI_Status) :: status
     242             : 
     243             : 
     244             :     if (present(arg1) .or. present(arg2) .or. present(arg3)) then
     245             :       call error_message("Error mo_objective_function: Received unexpected argument, check optimization settings")
     246             :     end if
     247             : 
     248             :     ! set these to nan so compiler does not complain
     249             :     if (present(arg2)) then
     250             :       arg2 = nodata_dp
     251             :     end if
     252             :     if (present(arg3)) then
     253             :       arg3 = nodata_dp
     254             :     end if
     255             : 
     256             :     call distribute_parameterset(parameterset)
     257             :     select case (opti_function)
     258             :     case (10 : 13, 17, 27 : 29)
     259             :       call MPI_Comm_size(domainMeta%comMaster, nproc, ierror)
     260             :       objective_master = 0.0_dp
     261             :       do iproc = 1, nproc - 1
     262             :         call MPI_Recv(partial_objective, 1, MPI_DOUBLE_PRECISION, iproc, 0, domainMeta%comMaster, status, ierror)
     263             :         objective_master = objective_master + partial_objective
     264             :       end do
     265             :       objective_master = objective_master**onesixth
     266             :     case (15)
     267             :       ! KGE for Q * RMSE for domain_avg TWS (standarized scored)
     268             :       call error_message("case 15, objective_kge_q_rmse_tws not implemented in parallel yet")
     269             :     case (30)
     270             :       ! KGE for Q * RMSE for domain_avg ET (standarized scored)
     271             :       ! objective_master = objective_kge_q_rmse_et(parameterset, eval)
     272             :       call message("case 30, objective_kge_q_rmse_et not implemented in parallel yet")
     273             :     case(33)
     274             :       call MPI_Comm_size(domainMeta%comMaster, nproc, ierror)
     275             :       objective_master = 0.0_dp
     276             :       multiple_master_objective(:) = 0.0_dp
     277             :       do iproc = 1, nproc - 1
     278             :         call MPI_Recv(multiple_partial_objective, 6, MPI_DOUBLE_PRECISION, iproc, 0, domainMeta%comMaster, status, ierror)
     279             :         multiple_master_objective = multiple_master_objective + multiple_partial_objective
     280             :       end do
     281             :       objective_master = objective_master + &
     282             :         (multiple_master_objective(1)+multiple_master_objective(2)+multiple_master_objective(3))
     283             :       objective_master = (objective_master/multiple_master_objective(4))**onesixth
     284             : 
     285             :     case default
     286             :       call error_message("Error objective_master: opti_function not implemented yet.")
     287             :     end select
     288             : 
     289             :     select case (opti_function)
     290             :     case(10)
     291             :       call message('    objective_sm_kge_catchment_avg = ', num2str(objective_master, '(F9.5)'))
     292             :     case(11)
     293             :       call message('    objective_sm_pd = ', num2str(objective_master, '(F9.5)'))
     294             :     case(12)
     295             :       call message('    objective_sm_sse_standard_score = ', num2str(objective_master, '(E12.5)'))
     296             :     case(13)
     297             :       call message('    objective_sm_corr = ', num2str(objective_master, '(F9.5)'))
     298             :     case(17)
     299             :       call message('    objective_neutrons_kge_catchment_avg = ', num2str(objective_master, '(F9.5)'))
     300             :     case(27)
     301             :       call message('    objective_et_kge_catchment_avg = ', num2str(objective_master, '(F9.5)'))
     302             :     case(28)
     303             :       call message('    objective_kge_q_sm_corr = ', num2str(objective_master, '(F9.5)'))
     304             :     case(29)
     305             :       call message('    objective_kge_q_et = ', num2str(objective_master, '(F9.5)'))
     306             :     case(33)
     307             :       call message('    objective_q_et_tws_kge_catchment_avg = ', num2str(objective_master, '(F9.5)'))
     308             :     case default
     309             :       call error_message("Error objective_master: opti_function not implemented yet, this part of the code should never execute.")
     310             :     end select
     311             : 
     312             :   END FUNCTION objective_master
     313             : 
     314             :   ! ------------------------------------------------------------------
     315             : 
     316             :   !    NAME
     317             :   !        objective_subprocess
     318             : 
     319             :   !    PURPOSE
     320             :   !>       \brief Wrapper for objective functions.
     321             : 
     322             :   !>       \details The function receives the parameterset from the master
     323             :   !>       process, selects the objective function case defined in a namelist,
     324             :   !>       i.e. the global variable \e opti\_function.
     325             :   !>       It returns the partial objective function value for a specific parameter set.
     326             : 
     327             :   !    INTENT(IN)
     328             :   !>       \param[in] "REAL(dp), DIMENSION(:) :: parameterset"
     329             :   !>       \param[in] "procedure(eval_interface) :: eval"
     330             : 
     331             :   !    INTENT(IN), OPTIONAL
     332             :   !>       \param[in] "real(dp), optional :: arg1"
     333             : 
     334             :   !    INTENT(OUT), OPTIONAL
     335             :   !>       \param[out] "real(dp), optional :: arg2"
     336             :   !>       \param[out] "real(dp), optional :: arg3"
     337             : 
     338             :   !    RETURN
     339             :   !>       \return real(dp) :: objective — objective function value
     340             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
     341             : 
     342             :   !    HISTORY
     343             :   !>       \authors Juliane Mai
     344             : 
     345             :   !>       \date Dec 2012
     346             : 
     347             :   ! Modifications:
     348             :   ! Stephan Thober Oct 2015 - moved all runoff related objective functions to mRM
     349             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
     350             :   ! Maren Kaluza Jun 2019 - parallel version
     351             : 
     352             :   subroutine objective_subprocess(eval, arg1, arg2, arg3)
     353             : 
     354             :     use mo_common_constants, only : nodata_dp
     355             :     use mo_common_mHM_mRM_variables, only : opti_function
     356             :     use mo_common_mpi_tools, only : get_parameterset
     357             :     use mo_common_variables, only : domainMeta
     358             :     use mpi_f08
     359             : 
     360             :     implicit none
     361             : 
     362             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     363             : 
     364             :     real(dp), optional, intent(in) :: arg1
     365             : 
     366             :     real(dp), optional, intent(out) :: arg2
     367             : 
     368             :     real(dp), optional, intent(out) :: arg3
     369             : 
     370             :     REAL(dp) :: partial_objective
     371             : 
     372             :     real(dp), dimension(6) :: multiple_partial_objective
     373             : 
     374             :     REAL(dp), DIMENSION(:), allocatable :: parameterset
     375             : 
     376             :     integer(i4) :: ierror
     377             : 
     378             :     type(MPI_Status) :: status
     379             : 
     380             :     logical :: do_obj_loop
     381             : 
     382             :     do ! a do loop without condition runs until exit
     383             :       call MPI_Recv(do_obj_loop, 1, MPI_LOGICAL, 0, 0, domainMeta%comMaster, status, ierror)
     384             : 
     385             :       if (.not. do_obj_loop) exit
     386             : 
     387             :       if (present(arg1) .or. present(arg2) .or. present(arg3)) then
     388             :         call error_message("Error mo_objective_function: Received unexpected argument, check optimization settings")
     389             :       end if
     390             : 
     391             :       ! set these to nan so compiler does not complain
     392             :       if (present(arg2)) then
     393             :         arg2 = nodata_dp
     394             :       end if
     395             :       if (present(arg3)) then
     396             :         arg3 = nodata_dp
     397             :       end if
     398             :       call get_parameterset(parameterset)
     399             :       select case (opti_function)
     400             :       case (10)
     401             :         ! KGE of catchment average SM
     402             :         partial_objective = objective_sm_kge_catchment_avg(parameterset, eval)
     403             :       case (11)
     404             :         ! pattern dissimilarity (PD) of SM fields
     405             :         partial_objective = objective_sm_pd(parameterset, eval)
     406             :       case (12)
     407             :         ! sum of squared errors of standard_score SM
     408             :         partial_objective = objective_sm_sse_standard_score(parameterset, eval)
     409             :       case (13)
     410             :         ! soil moisture correlation - temporal
     411             :         partial_objective = objective_sm_corr(parameterset, eval)
     412             :       case (15)
     413             :         ! KGE for Q * RMSE for domain_avg TWS (standarized scored)
     414             :         ! partial_objective = objective_kge_q_rmse_tws(parameterset, eval)
     415             :         call error_message("Error objective_subprocess: case 15 not supported with MPI.")
     416             :       case (17)
     417             :         ! KGE of catchment average SM
     418             :         partial_objective = objective_neutrons_kge_catchment_avg(parameterset, eval)
     419             :       case (27)
     420             :         ! KGE of catchment average ET
     421             :         partial_objective = objective_et_kge_catchment_avg(parameterset, eval)
     422             :       case (28)
     423             :         !  KGE for Q + SSE for SM (standarized scored)
     424             :         partial_objective = objective_kge_q_sm_corr(parameterset, eval)
     425             :       case (29)
     426             :         !  KGE for Q + KGE of catchment average ET
     427             :         partial_objective = objective_kge_q_et(parameterset, eval)
     428             :       case (30)
     429             :         ! KGE for Q * RMSE for domain_avg ET (standarized scored)
     430             :         ! partial_objective = objective_kge_q_rmse_et(parameterset, eval)
     431             :         call error_message("Error objective_subprocess: case 30 not supported with MPI.")
     432             :       case(33)
     433             :         multiple_partial_objective = objective_q_et_tws_kge_catchment_avg(parameterset, eval)
     434             :       case default
     435             :         call error_message("Error objective_subprocess: opti_function not implemented yet.")
     436             :       end select
     437             : 
     438             :       select case (opti_function)
     439             :       case (10 : 13, 17, 27 : 29)
     440             :         call MPI_Send(partial_objective,1, MPI_DOUBLE_PRECISION,0,0,domainMeta%comMaster,ierror)
     441             :       case(33)
     442             :         call MPI_Send(multiple_partial_objective, 6, MPI_DOUBLE_PRECISION,0,0,domainMeta%comMaster,ierror)
     443             :       case default
     444             :         call error_message("Error objective_subprocess: this part should not be executed -> error in the code.")
     445             :       end select
     446             : 
     447             :       deallocate(parameterset)
     448             :     end do
     449             : 
     450             :   END subroutine objective_subprocess
     451             : 
     452             : #endif
     453             :   ! ------------------------------------------------------------------
     454             : 
     455             :   !    NAME
     456             :   !        objective_sm_kge_catchment_avg
     457             : 
     458             :   !    PURPOSE
     459             :   !>       \brief Objective function for soil moisture.
     460             : 
     461             :   !>       \details The objective function only depends on a parameter vector.
     462             :   !>       The model will be called with that parameter vector and
     463             :   !>       the model output is subsequently compared to observed data.
     464             : 
     465             :   !>       Therefore, the Kling-Gupta model efficiency \f$ KGE \f$ of the catchment average
     466             :   !>       soil mloisture (SM) is calculated
     467             :   !>       \f[ KGE = 1.0 - \sqrt{( (1-r)^2 + (1-\alpha)^2 + (1-\beta)^2 )} \f]
     468             :   !>       where
     469             :   !>       \f$ r \f$ = Pearson product-moment correlation coefficient
     470             :   !>       \f$ \alpha \f$ = ratio of simulated mean to observed mean SM
     471             :   !>       \f$ \beta  \f$ = ratio of similated standard deviation to observed standard deviation
     472             :   !>       is calculated and the objective function for a given domain \f$ i \f$ is
     473             :   !>       \f[ \phi_{i} = 1.0 - KGE_{i} \f]
     474             :   !>       \f$ \phi_{i} \f$ is the objective since we always apply minimization methods.
     475             :   !>       The minimal value of \f$ \phi_{i} \f$ is 0 for the optimal KGE of 1.0.
     476             : 
     477             :   !>       Finally, the overall objective function value \f$ OF \f$ is estimated based on the power-6
     478             :   !>       norm to combine the \f$ \phi_{i} \f$ from all domains \f$ N \f$.
     479             :   !>       \f[ OF = \sqrt[6]{\sum((1.0 - KGE_{i})/N)^6 }.  \f]
     480             :   !>       The observed data L1_sm, L1_sm_mask are global in this module.
     481             : 
     482             :   !    INTENT(IN)
     483             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
     484             :   !>       \param[in] "procedure(eval_interface) :: eval"
     485             : 
     486             :   !    RETURN
     487             :   !>       \return real(dp) :: objective_sm_kge_catchment_avg — objective function value
     488             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
     489             : 
     490             :   !    HISTORY
     491             :   !>       \authors Matthias Zink
     492             : 
     493             :   !>       \date May 2015
     494             : 
     495             :   ! Modifications:
     496             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
     497             : 
     498           0 :   FUNCTION objective_sm_kge_catchment_avg(parameterset, eval)
     499             : 
     500          25 :     use mo_optimization_types, only : optidata_sim
     501             :     use mo_common_constants, only : nodata_dp
     502             :     use mo_common_variables, only : level1, domainMeta
     503             :     use mo_errormeasures, only : KGE
     504             :     use mo_global_variables, only : L1_smObs
     505             :     use mo_moment, only : average
     506             :     use mo_string_utils, only : num2str
     507             : 
     508             :     implicit none
     509             : 
     510             :     real(dp), dimension(:), intent(in) :: parameterset
     511             : 
     512             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     513             : 
     514             :     real(dp) :: objective_sm_kge_catchment_avg
     515             : 
     516             :     ! domain loop counter
     517             :     integer(i4) :: iDomain
     518             : 
     519             :     ! time loop counter
     520             :     integer(i4) :: iTime
     521             : 
     522             :     ! number of time steps in simulated SM
     523             :     integer(i4) :: n_time_steps
     524             : 
     525             :     ! ncells1 of level 1
     526             :     integer(i4) :: ncells1
     527             : 
     528             :     ! number of invalid timesteps
     529           0 :     real(dp) :: invalid_times
     530             : #ifndef MPI
     531             :     ! for sixth root
     532             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
     533             : #endif
     534             : 
     535             :     ! spatial average of observed soil moisture
     536           0 :     real(dp), dimension(:), allocatable :: sm_catch_avg_domain
     537             : 
     538             :     ! spatial avergae of modeled  soil moisture
     539           0 :     real(dp), dimension(:), allocatable :: sm_opti_catch_avg_domain
     540             : 
     541           0 :     type(optidata_sim), dimension(:), allocatable :: smOptiSim
     542             : 
     543             :     ! mask for valid sm catchment avg time steps
     544           0 :     logical, dimension(:), allocatable :: mask_times
     545             : 
     546             : 
     547           0 :     allocate(smOptiSim(domainMeta%nDomains))
     548           0 :     call eval(parameterset, smOptiSim = smOptiSim)
     549             : 
     550             :     ! initialize some variables
     551           0 :     objective_sm_kge_catchment_avg = 0.0_dp
     552             : 
     553             :     ! loop over domain - for applying power law later on
     554           0 :     do iDomain = 1, domainMeta%nDomains
     555             : 
     556             :       ! get domain information
     557           0 :       ncells1 = level1(iDomain)%ncells
     558             : 
     559             :       ! allocate
     560           0 :       allocate(mask_times              (size(smOptiSim(iDomain)%dataSim, dim = 2)))
     561           0 :       allocate(sm_catch_avg_domain     (size(smOptiSim(iDomain)%dataSim, dim = 2)))
     562           0 :       allocate(sm_opti_catch_avg_domain(size(smOptiSim(iDomain)%dataSim, dim = 2)))
     563             : 
     564             :       ! initalize
     565           0 :       mask_times = .TRUE.
     566           0 :       sm_catch_avg_domain = nodata_dp
     567           0 :       sm_opti_catch_avg_domain = nodata_dp
     568             : 
     569           0 :       invalid_times = 0.0_dp
     570             :       ! calculate catchment average soil moisture
     571           0 :       n_time_steps = size(smOptiSim(iDomain)%dataSim, dim = 2)
     572           0 :       do iTime = 1, n_time_steps
     573             : 
     574             :         ! check for enough data points in timesteps for KGE calculation
     575             :         ! more then 10 percent avaiable in current field
     576           0 :         if (count(L1_smObs(iDomain)%maskObs(:, iTime)) .LE. (0.10_dp * real(nCells1, dp))) then
     577           0 :           invalid_times = invalid_times + 1.0_dp
     578           0 :           mask_times(iTime) = .FALSE.
     579           0 :           cycle
     580             :         end if
     581           0 :         sm_catch_avg_domain(iTime) = average(L1_smObs(iDomain)%dataObs(:, iTime), mask = L1_smObs(iDomain)%maskObs(:, iTime))
     582           0 :         sm_opti_catch_avg_domain(iTime) = average(smOptiSim(iDomain)%dataSim(:, iTime), mask = L1_smObs(iDomain)%maskObs(:, iTime))
     583             :       end do
     584             : 
     585             :       ! user information about invalid times
     586           0 :       if (invalid_times .GT. 0.5_dp) then
     587           0 :         call message('   WARNING: objective_sm: Detected invalid timesteps (.LT. 10 valid data points).')
     588             :         call message('                          Fraction of invalid timesteps: ', &
     589           0 :                 num2str(invalid_times / real(n_time_steps, dp), '(F4.2)'))
     590             :       end if
     591             : 
     592             : 
     593             :       ! calculate average soil moisture KGE over all domains with power law
     594             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
     595             :       objective_sm_kge_catchment_avg = objective_sm_kge_catchment_avg + &
     596             :               ((1.0_dp - KGE(sm_catch_avg_domain, sm_opti_catch_avg_domain, mask = mask_times)) / &
     597           0 :                         real(domainMeta%overallNumberOfDomains, dp))**6
     598             : 
     599             :       ! deallocate
     600           0 :       deallocate(mask_times)
     601           0 :       deallocate(sm_catch_avg_domain)
     602           0 :       deallocate(sm_opti_catch_avg_domain)
     603           0 :       call smOptiSim(iDomain)%destroy()
     604             :     end do
     605           0 :     deallocate(smOptiSim)
     606             : 
     607             : #ifndef MPI
     608           0 :     objective_sm_kge_catchment_avg = objective_sm_kge_catchment_avg**onesixth
     609             : 
     610           0 :     call message('    objective_sm_kge_catchment_avg = ', num2str(objective_sm_kge_catchment_avg, '(F9.5)'))
     611             : #endif
     612             : 
     613             : 
     614           0 :   END FUNCTION objective_sm_kge_catchment_avg
     615             : 
     616             :   ! ------------------------------------------------------------------
     617             : 
     618             :   !    NAME
     619             :   !        objective_q_et_tws_kge_catchment_avg
     620             : 
     621             :   !    PURPOSE
     622             :   !>       \brief Objective function for et, tws and q.
     623             : 
     624             :   !>       \details The feature of this objective function is the
     625             :   !>                separation of the eval call into four
     626             :   !>                calls, each with another index list. The subroutine eval then only
     627             :   !>                uses the indices from that index list internally instead of having loops
     628             :   !>                over all domains. The integer array domainMeta%optidata decides which
     629             :   !>                indices to use. Therefore the array is split into disjunct subsets, and,
     630             :   !>                if chosen wisely in the namelist, also covers all domains.
     631             :   !>
     632             :   !>                With this the eval calls sum up in a way that for each domain eval was
     633             :   !>                called at most once, but for different opti_data.
     634             : 
     635             :   !    HISTORY
     636             :   !>       \authors Maren Kaluza
     637             : 
     638             :   !>       \date July 2019
     639             : 
     640             :   ! Modifications:
     641             : 
     642          70 :   FUNCTION objective_q_et_tws_kge_catchment_avg(parameterset, eval)
     643             : 
     644           0 :     use mo_optimization_types, only : optidata_sim
     645             :     use mo_common_constants, only : nodata_dp
     646             :     use mo_common_variables, only : domainMeta
     647             :     use mo_global_variables, only : L1_etObs, L1_twsaObs
     648             :     use mo_errormeasures, only : kge
     649             :     use mo_moment, only : average
     650             :     use mo_string_utils, only : num2str
     651             :     use mo_mrm_objective_function_runoff, only : extract_runoff
     652             : 
     653             :     implicit none
     654             : 
     655             :     !> the parameterset passed to the eval subroutine
     656             :     real(dp), dimension(:), intent(in) :: parameterset
     657             :     !> the eval subroutine called by this objective function
     658             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     659             :     !> the return value of the objective function. In this case it is
     660             :     !> an array to provide the possibility to weight the outcome accordingly
     661             :     real(dp), dimension(6) :: objective_q_et_tws_kge_catchment_avg
     662             : 
     663             :     !> domain loop counter
     664             :     integer(i4) :: iDomain
     665             : 
     666             :     !> counter for short loops
     667             :     integer(i4) :: i
     668             : #ifndef MPI
     669             :     !> for sixth root
     670             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
     671             : #endif
     672             : 
     673             :     !> modelled runoff for a given parameter set
     674             :     ! dim1=nTimeSteps, dim2=nGauges
     675           7 :     real(dp), allocatable, dimension(:, :) :: runoff
     676             :     !> number of all gauges, aquired via runoff
     677             :     integer(i4) :: nGaugesTotal
     678             : 
     679             :     !> aggregated simulated runoff
     680           7 :     real(dp), dimension(:), allocatable :: runoff_agg
     681             : 
     682             :     !> measured runoff
     683           7 :     real(dp), dimension(:), allocatable :: runoff_obs
     684             : 
     685             :     !> mask for measured runoff
     686           7 :     logical, dimension(:), allocatable :: runoff_obs_mask
     687             : 
     688             :     !> kge_q(nGaugesTotal)
     689          49 :     real(dp) :: kge_q
     690             : 
     691             :     !> gauges counter
     692             :     integer(i4) :: gg, iCell
     693             : 
     694             :     !> number of q domains
     695             :     integer(i4) :: nQDomains
     696             : 
     697             :     !> number of et domains
     698             :     integer(i4) :: nEtDomains
     699             : 
     700             :     !> number of tws domains
     701             :     integer(i4) :: nTwsDomains
     702             : 
     703             :     !> number of TWS and ET domains (providing both)
     704             :     integer(i4) :: nEtTwsDomains
     705             : 
     706             :     !> index array of ET domains
     707           7 :     integer(i4), dimension(:), allocatable :: opti_domain_indices_ET
     708             : 
     709             :     !> index array of TWS domains
     710           7 :     integer(i4), dimension(:), allocatable :: opti_domain_indices_TWS
     711             : 
     712             :     !> index array of TWS and ET domains (providing both)
     713           7 :     integer(i4), dimension(:), allocatable :: opti_domain_indices_ET_TWS
     714             : 
     715             :     !> index array of ET domains
     716           7 :     integer(i4), dimension(:), allocatable :: opti_domain_indices_Q
     717             : 
     718             :     !> simulated et
     719           7 :     type(optidata_sim), dimension(:), allocatable :: etOptiSim
     720             : 
     721             :     !> simulated tws
     722           7 :     type(optidata_sim), dimension(:), allocatable :: twsOptiSim
     723             : 
     724             :     !> simulated twsa (anomaly)
     725           7 :     type(optidata_sim), dimension(:), allocatable :: twsaOptiSim
     726             : 
     727          49 :     real(dp) :: kge_tws
     728             : 
     729          49 :     real(dp) :: kge_et
     730             : 
     731             :     integer(i4) :: numberOfSummands
     732             : 
     733             : 
     734             :     ! initialize some variables
     735          49 :     objective_q_et_tws_kge_catchment_avg(:) = 0.0_dp
     736           7 :     kge_tws = 0.0_dp
     737           7 :     kge_et = 0.0_dp
     738           7 :     kge_q = 0.0_dp
     739           7 :     numberOfSummands = 0
     740             :     !--------------------------------------------
     741             :     ! ET & TWS
     742             :     !--------------------------------------------
     743             :     ! eval runs to get simulated output for et and tws
     744             :     ! before each eval call we generate an index list of the domains for which
     745             :     ! eval should be called. Read details for further information
     746           7 :     call init_indexarray_for_opti_data(domainMeta, 6, nEtTwsDomains, opti_domain_indices_ET_TWS)
     747           7 :     if (nEtTwsDomains > 0) then
     748          63 :       allocate( etOptiSim(domainMeta%nDomains))
     749          63 :       allocate(twsOptiSim(domainMeta%nDomains))
     750          63 :       allocate(twsaOptiSim(domainMeta%nDomains))
     751             :       call eval(parameterset, opti_domain_indices = opti_domain_indices_ET_TWS, &
     752           7 :                                                  twsOptiSim = twsOptiSim, etOptiSim = etOptiSim)
     753             :       ! for all domains that have ET and TWS
     754          14 :       do i = 1, size(opti_domain_indices_ET_TWS)
     755           7 :         iDomain = opti_domain_indices_ET_TWS(i)
     756           7 :         call convert_tws_to_twsa(twsOptiSim(iDomain), L1_twsaObs(iDomain), twsaOptiSim(iDomain))
     757         245 :         do iCell = 1, size(L1_etObs(iDomain)%maskObs(:, :), dim = 1)
     758             :           kge_et = kge_et + &
     759           0 :             (1.0_dp - KGE(L1_etObs(iDomain)%dataObs(iCell, :), etOptiSim(iDomain)%dataSim(iCell, :),&
     760         238 :                            mask = L1_etObs(iDomain)%maskObs(iCell, :)))**6
     761         245 :           numberOfSummands = numberOfSummands + 1
     762             :         end do
     763         245 :         do iCell = 1, size(L1_twsaObs(iDomain)%maskObs(:, :), dim = 1)
     764             :           kge_tws = kge_tws + &
     765           0 :             (1.0_dp - KGE(L1_twsaObs(iDomain)%dataObs(iCell, :), twsaOptiSim(iDomain)%dataSim(iCell, :),&
     766         238 :                            mask = L1_twsaObs(iDomain)%maskObs(iCell, :)))**6
     767         245 :           numberOfSummands = numberOfSummands + 1
     768             :         end do
     769             :         ! deallocate
     770           7 :         call etOptiSim(iDomain)%destroy()
     771           7 :         call twsOptiSim(iDomain)%destroy()
     772          21 :         call twsaOptiSim(iDomain)%destroy()
     773             :       end do
     774          49 :       deallocate(etOptiSim)
     775          49 :       deallocate(twsOptiSim)
     776          49 :       deallocate(twsaOptiSim)
     777             :      ! write(0,*) 'nEtTwsDomains, kge_tws', nEtTwsDomains, kge_tws
     778             :      ! write(0,*) 'nEtTwsDomains, kge_et', nEtTwsDomains, kge_et
     779             :     end if
     780             :     !--------------------------------------------
     781             :     ! TWS
     782             :     !--------------------------------------------
     783             :     ! eval runs to get simulated output for tws
     784             :     ! before each eval call we generate an index list of the domains for which
     785             :     ! eval should be called. Read details for further information
     786           7 :     call init_indexarray_for_opti_data(domainMeta, 3, nTwsDomains, opti_domain_indices_TWS)
     787           7 :     if (nTwsDomains > 0) then
     788          63 :       allocate(twsOptiSim(domainMeta%nDomains))
     789          63 :       allocate(twsaOptiSim(domainMeta%nDomains))
     790           7 :       call eval(parameterset, opti_domain_indices = opti_domain_indices_TWS, twsOptiSim = twsOptiSim)
     791             :       ! for all domains that have ET and TWS
     792          21 :       do i = 1, size(opti_domain_indices_TWS)
     793          14 :         iDomain = opti_domain_indices_TWS(i)
     794          14 :         call convert_tws_to_twsa(twsOptiSim(iDomain), L1_twsaObs(iDomain), twsaOptiSim(iDomain))
     795         490 :         do iCell = 1, size(L1_twsaObs(iDomain)%maskObs(:, :), dim = 1)
     796             :           kge_tws = kge_tws + &
     797           0 :             (1.0_dp - KGE(L1_twsaObs(iDomain)%dataObs(iCell, :), twsaOptiSim(iDomain)%dataSim(iCell, :),&
     798         476 :                            mask = L1_twsaObs(iDomain)%maskObs(iCell, :)))**6
     799         490 :           numberOfSummands = numberOfSummands + 1
     800             :         end do
     801          35 :         call twsOptiSim(iDomain)%destroy()
     802             :       end do
     803          49 :       deallocate(twsOptiSim)
     804             :     !  write(0,*) 'nTwsDomains, kge_tws', nTwsDomains, kge_tws
     805             :     end if
     806           7 :     objective_q_et_tws_kge_catchment_avg(2) = kge_tws
     807             : 
     808             :     !--------------------------------------------
     809             :     ! ET
     810             :     !--------------------------------------------
     811             :     ! eval runs to get simulated output for et
     812             :     ! before each eval call we generate an index list of the domains for which
     813             :     ! eval should be called. Read details for further information
     814           7 :     call init_indexarray_for_opti_data(domainMeta, 5, nEtDomains, opti_domain_indices_ET)
     815           7 :     if (nEtDomains > 0) then
     816          63 :       allocate(etOptiSim(domainMeta%nDomains))
     817           7 :       call eval(parameterset, opti_domain_indices = opti_domain_indices_ET, etOptiSim = etOptiSim)
     818             :       ! for all domains that have ET and TWS
     819          21 :       do i = 1, size(opti_domain_indices_ET)
     820          14 :         iDomain = opti_domain_indices_ET(i)
     821         490 :         do iCell = 1, size(L1_etObs(iDomain)%maskObs(:, :), dim = 1)
     822             :           kge_et = kge_et + &
     823           0 :             (1.0_dp - KGE(L1_etObs(iDomain)%dataObs(iCell, :), etOptiSim(iDomain)%dataSim(iCell, :),&
     824         476 :                            mask = L1_etObs(iDomain)%maskObs(iCell, :)))**6
     825         490 :           numberOfSummands = numberOfSummands + 1
     826             :         end do
     827          21 :         call etOptiSim(iDomain)%destroy()
     828             :       end do
     829          49 :       deallocate(etOptiSim)
     830             :      ! write(0,*) 'nEtDomains, kge_et', nEtDomains, kge_et
     831             :     end if
     832           7 :     objective_q_et_tws_kge_catchment_avg(3) = kge_et
     833             : 
     834             :     !--------------------------------------------
     835             :     ! RUNOFF
     836             :     !--------------------------------------------
     837             :     ! eval runs to get simulated output for runoff
     838             :     ! before the eval call we generate an index list of the domains for which
     839             :     ! eval should be called. Read details for further information
     840             :     ! ToDo:  The arrays for qTin, qTout, will be rewritten in the other calls when
     841             :     ! Q is not called last. Change that for more flexibility
     842           7 :     call init_indexarray_for_opti_data(domainMeta, 1, nQDomains, opti_domain_indices_Q)
     843             : 
     844           7 :     if (nQDomains > 0) then
     845           7 :       call eval(parameterset, opti_domain_indices = opti_domain_indices_Q, runoff = runoff)
     846           7 :       nGaugesTotal = size(runoff, dim = 2)
     847          14 :       do gg = 1, nGaugesTotal
     848             : 
     849             :         ! extract runoff
     850           7 :         call extract_runoff(gg, runoff, runoff_agg, runoff_obs, runoff_obs_mask)
     851             : 
     852             :         kge_q = kge_q + &
     853           7 :               (1.0_dp - kge(runoff_obs, runoff_agg, mask = runoff_obs_mask))**6
     854           7 :         numberOfSummands = numberOfSummands + 1
     855          21 :         deallocate (runoff_agg, runoff_obs, runoff_obs_mask)
     856             :       end do
     857             :      ! write(0,*) 'nQDomains, kge_q', nQDomains, kge_q
     858             :     end if
     859           7 :     objective_q_et_tws_kge_catchment_avg(1) = kge_q
     860             : 
     861           7 :     objective_q_et_tws_kge_catchment_avg(4) = real(numberOfSummands, dp)
     862             : 
     863             : 
     864             : #ifndef MPI
     865           7 :     objective_q_et_tws_kge_catchment_avg(1) = ((kge_q+kge_et+kge_tws)/real(numberOfSummands, dp))**onesixth
     866             : 
     867             :     call message('    objective_q_et_tws_kge_catchment_avg = ', &
     868           7 :                       num2str(objective_q_et_tws_kge_catchment_avg(1), '(F9.5)'))
     869             : #endif
     870             : 
     871           7 :   END FUNCTION objective_q_et_tws_kge_catchment_avg
     872             : 
     873             :   ! ------------------------------------------------------------------
     874             : 
     875             :   !    NAME
     876             :   !        init_indexarray_for_opti_data
     877             : 
     878             :   !    PURPOSE
     879             :   !>       \brief creates an index array of the inidices of the domains eval
     880             :   !>              should MPI process.
     881             :   !
     882             :   !>       \details The data type domainMeta contains an array optidata of size
     883             :   !>                domainMeta%nDomains, telling us, which domains should be
     884             :   !>                optimized with which opti_data. This subroutine splits all
     885             :   !>                domains assigned to a process and returns an index list
     886             :   !>                corresponding to the value of domainMeta%optidata.
     887             :   !>
     888             :   !>                The index array opti_domain_indices can then be passed
     889             :   !>                as an optional argument to the eval subroutine. The
     890             :   !>                eval then instead of using loops over all domains only
     891             :   !>                uses the passed indices.
     892             :   !>
     893             :   !>                This subroutine also returns the size of that array since it
     894             :   !>                helps with the calculations of the optimization in the end.
     895             : 
     896             :   !    HISTORY
     897             :   !>       \authors Maren Kaluza
     898             : 
     899             :   !>       \date July 2019
     900          28 :   subroutine init_indexarray_for_opti_data(domainMeta, optidataOption, nOptiDomains, opti_domain_indices)
     901           7 :     use mo_common_types, only: domain_meta
     902             :     !> meta data for all domains assigned to that process
     903             :     type(domain_meta),                                intent(in)    :: domainMeta
     904             :     !> which opti data should be used in the eval called after calling this subroutine
     905             :     integer(i4),                                      intent(in)    :: optidataOption
     906             :     !> number of domains that will be optimized in the following eval call
     907             :     integer(i4),                                      intent(out)   :: nOptiDomains
     908             :     !> the indices of the domains that are to be processed in the following eval call
     909             :     integer(i4), dimension(:), allocatable,           intent(out)   :: opti_domain_indices
     910             : 
     911             :     !> domain loop counter
     912             :     integer(i4) :: iDomain, i
     913             : 
     914           0 :     if (allocated(opti_domain_indices)) deallocate(opti_domain_indices)
     915             :     ! count domains on MPI process that use optidata
     916          28 :     nOptiDomains = 0
     917         196 :     do iDomain = 1, domainMeta%nDomains
     918         196 :       if (domainMeta%optidata(iDomain) == optidataOption) nOptiDomains = nOptiDomains + 1
     919             :     end do
     920             :     ! write indices of these domains into an array
     921          28 :     if (nOptiDomains > 0) then
     922          84 :       allocate(opti_domain_indices(nOptiDomains))
     923          28 :       i = 0
     924         196 :       do iDomain = 1, domainMeta%nDomains
     925         196 :         if (domainMeta%optidata(iDomain) == optidataOption) then
     926          42 :           i = i + 1
     927          42 :           opti_domain_indices(i) = iDomain
     928             :         end if
     929             :       end do
     930             :     end if
     931          28 :   end subroutine init_indexarray_for_opti_data
     932             :   ! ------------------------------------------------------------------
     933             : 
     934             :   !    NAME
     935             :   !        objective_sm_corr
     936             : 
     937             :   !    PURPOSE
     938             :   !>       \brief Objective function for soil moisture.
     939             : 
     940             :   !>       \details The objective function only depends on a parameter vector.
     941             :   !>       The model will be called with that parameter vector and
     942             :   !>       the model output is subsequently compared to observed data.
     943             : 
     944             :   !>       Therefore the Pearson correlation between observed and modeled soil
     945             :   !>       moisture on each grid cell \f$ j \f$ is compared
     946             :   !>       \f[ r_j = r^2(SM_{obs}^j, SM_{sim}^j) \f]
     947             :   !>       where
     948             :   !>       \f$ r^2\f$        = Pearson correlation coefficient,
     949             :   !>       \f$ SM_{obs} \f$  = observed soil moisture,
     950             :   !>       \f$ SM_{sim}  \f$ = simulated soil moisture.
     951             :   !>       The observed data \f$ SM_{obs} \f$ are global in this module.
     952             : 
     953             :   !>       The the correlation is spatially averaged as
     954             :   !>       \f[ \phi_{i} = \frac{1}{K} \cdot \sum_{j=1}^K r_j \f]
     955             :   !>       where \f$ K \f$ denotes the number of valid cells in the study domain.
     956             :   !>       Finally, the overall objective function value \f$ OF \f$ is estimated based on the power-6
     957             :   !>       norm to combine the \f$ \phi_{i} \f$ from all domains \f$ N \f$.
     958             :   !>       \f[ OF = \sqrt[6]{\sum((1.0 - \phi_{i})/N)^6 }. \f]
     959             :   !>       The observed data L1_sm, L1_sm_mask are global in this module.
     960             : 
     961             :   !    INTENT(IN)
     962             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
     963             :   !>       \param[in] "procedure(eval_interface) :: eval"
     964             : 
     965             :   !    RETURN
     966             :   !>       \return real(dp) :: objective_sm_corr — objective function value
     967             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
     968             : 
     969             :   !    HISTORY
     970             :   !>       \authors Matthias Zink
     971             : 
     972             :   !>       \date March 2015
     973             : 
     974             :   ! Modifications:
     975             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
     976             : 
     977           0 :   FUNCTION objective_sm_corr(parameterset, eval)
     978             : 
     979          28 :     use mo_optimization_types, only : optidata_sim
     980             :     use mo_common_variables, only : level1, domainMeta
     981             :     use mo_global_variables, only : L1_smObs
     982             :     use mo_moment, only : correlation
     983             :     use mo_string_utils, only : num2str
     984             : 
     985             :     implicit none
     986             : 
     987             :     real(dp), dimension(:), intent(in) :: parameterset
     988             : 
     989             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     990             : 
     991             :     real(dp) :: objective_sm_corr
     992             : 
     993             :     ! domain loop counter
     994             :     integer(i4) :: iDomain
     995             : 
     996             :     ! cell loop counter
     997             :     integer(i4) :: iCell
     998             : 
     999             :     ! ncells1 of level 1
    1000             :     integer(i4) :: ncells1
    1001             : 
    1002             :     ! number of invalid cells in catchment
    1003           0 :     real(dp) :: invalid_cells
    1004             : 
    1005             :     ! domains wise objectives
    1006           0 :     real(dp) :: objective_sm_corr_domain
    1007             : 
    1008             : #ifndef MPI
    1009             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    1010             : #endif
    1011             : 
    1012           0 :     type(optidata_sim), dimension(:), allocatable :: smOptiSim
    1013             : 
    1014             : 
    1015           0 :     allocate(smOptiSim(domainMeta%nDomains))
    1016           0 :     call eval(parameterset, smOptiSim = smOptiSim)
    1017             : 
    1018             :     ! initialize some variables
    1019           0 :     objective_sm_corr = 0.0_dp
    1020             : 
    1021             :     ! loop over domain - for applying power law later on
    1022           0 :     do iDomain = 1, domainMeta%nDomains
    1023             : 
    1024             :       ! init
    1025           0 :       objective_sm_corr_domain = 0.0_dp
    1026             :       ! get domain information
    1027           0 :       ncells1 = level1(iDomain)%ncells
    1028             : 
    1029           0 :       invalid_cells = 0.0_dp
    1030             :       ! temporal correlation is calculated on individual gridd cells
    1031             : 
    1032           0 :       do iCell = 1, size(L1_smObs(iDomain)%maskObs(:, :), dim = 1)
    1033             : 
    1034             :         ! check for enough data points in time for correlation
    1035           0 :         if (count(L1_smObs(iDomain)%maskObs(iCell, :)) .LE. 0.10_dp * real(size(L1_smObs(iDomain)%dataObs(:, :), dim = 2), dp)) then
    1036           0 :           invalid_cells = invalid_cells + 1.0_dp
    1037           0 :           cycle
    1038             :         end if
    1039             :         objective_sm_corr_domain = objective_sm_corr_domain + &
    1040           0 :                 correlation(L1_smObs(iDomain)%dataObs(iCell, :), smOptiSim(iDomain)%dataSim(iCell, :), &
    1041           0 :                                                            mask = L1_smObs(iDomain)%maskObs(iCell, :))
    1042             :       end do
    1043             : 
    1044             :       ! user information about invalid cells
    1045           0 :       if (invalid_cells .GT. 0.5_dp) then
    1046           0 :         call message('   WARNING: objective_sm: Detected invalid cells in study area (.LT. 10 valid data points).')
    1047             :         call message('                          Fraction of invalid cells: ', &
    1048           0 :                 num2str(invalid_cells / real(nCells1, dp), '(F4.2)'))
    1049             :       end if
    1050             : 
    1051             : 
    1052             :       ! calculate average soil moisture correlation over all domains with power law
    1053             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    1054             :       objective_sm_corr = objective_sm_corr + &
    1055           0 :               ((1.0_dp - objective_sm_corr_domain / real(nCells1, dp)) / real(domainMeta%overallNumberOfDomains, dp))**6
    1056             :     end do
    1057             : #ifndef MPI
    1058           0 :     objective_sm_corr = objective_sm_corr**onesixth
    1059             : 
    1060           0 :     call message('    objective_sm_corr = ', num2str(objective_sm_corr, '(F9.5)'))
    1061             : #endif
    1062             : 
    1063           0 :   END FUNCTION objective_sm_corr
    1064             : 
    1065             :   ! ------------------------------------------------------------------
    1066             : 
    1067             :   !    NAME
    1068             :   !        objective_sm_pd
    1069             : 
    1070             :   !    PURPOSE
    1071             :   !>       \brief Objective function for soil moisture.
    1072             : 
    1073             :   !>       \details The objective function only depends on a parameter vector.
    1074             :   !>       The model will be called with that parameter vector and
    1075             :   !>       the model output is subsequently compared to observed data.
    1076             : 
    1077             :   !>       Therefore the Pattern Dissimilarity (PD) of observed and modeled soil
    1078             :   !>       moisture fields is calculated - aim: matching spatial patters
    1079             :   !>       \f[ E(t) = PD\left( SM_{obs}(t), SM_{sim}(t) \right) \f]
    1080             :   !>       where
    1081             :   !>       \f$ PD \f$        = pattern dissimilarity function,
    1082             :   !>       \f$ SM_{obs} \f$  = observed soil moisture,
    1083             :   !>       \f$ SM_{sim}  \f$ = simulated soil moisture.
    1084             :   !>       \f$ E(t)  \f$     = pattern dissimilarity at timestep \f$ t \f$.
    1085             :   !>       The the pattern dissimilaity (E) is spatially averaged as
    1086             :   !>       \f[ \phi_{i} = \frac{1}{T} \cdot \sum_{t=1}^T E_t \f]
    1087             :   !>       where \f$ T \f$ denotes the number of time steps.
    1088             :   !>       Finally, the overall objective function value \f$ OF \f$ is estimated based on the power-6
    1089             :   !>       norm to combine the \f$ \phi_{i} \f$ from all domains \f$ N \f$.
    1090             :   !>       \f[ OF = \sqrt[6]{\sum((1.0 - \phi_{i})/N)^6 } . \f]
    1091             :   !>       The observed data L1_sm, L1_sm_mask are global in this module.
    1092             :   !>       The observed data L1_sm, L1_sm_mask are global in this module.
    1093             : 
    1094             :   !    INTENT(IN)
    1095             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    1096             :   !>       \param[in] "procedure(eval_interface) :: eval"
    1097             : 
    1098             :   !    RETURN
    1099             :   !>       \return real(dp) :: objecive_sm_pd — objective function value
    1100             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    1101             : 
    1102             :   !    HISTORY
    1103             :   !>       \authors Matthias Zink
    1104             : 
    1105             :   !>       \date May 2015
    1106             : 
    1107             :   ! Modifications:
    1108             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    1109             : 
    1110           0 :   FUNCTION objective_sm_pd(parameterset, eval)
    1111             : 
    1112           0 :     use mo_optimization_types, only : optidata_sim
    1113             :     use mo_common_constants, only : nodata_dp
    1114             :     use mo_common_variables, only : level1, domainMeta
    1115             :     use mo_global_variables, only : L1_smObs
    1116             :     use mo_spatialsimilarity, only : PD
    1117             :     use mo_string_utils, only : num2str
    1118             : 
    1119             :     implicit none
    1120             : 
    1121             :     real(dp), dimension(:), intent(in) :: parameterset
    1122             : 
    1123             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1124             : 
    1125             :     ! objective function value
    1126             :     real(dp) :: objective_sm_pd
    1127             : 
    1128             :     ! domain loop counter
    1129             :     integer(i4) :: iDomain
    1130             : 
    1131             :     ! time loop counter
    1132             :     integer(i4) :: iTime
    1133             : 
    1134             :     ! level 1 number of culomns and rows
    1135             :     integer(i4) :: nrows1, ncols1
    1136             : 
    1137             :     ! for sixth root
    1138             : #ifndef MPI
    1139             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    1140             : #endif
    1141             : 
    1142             :     ! matrices of SM from vectorized arrays
    1143           0 :     real(dp), dimension(:, :), allocatable :: mat1, mat2
    1144             : 
    1145             :     ! pattern dissimilarity (pd) at every time step
    1146           0 :     real(dp), dimension(:), allocatable :: pd_time_series
    1147             : 
    1148             :     ! simulated soil moisture
    1149           0 :     type(optidata_sim), dimension(:), allocatable :: smOptiSim
    1150             : 
    1151             :     ! mask of valid cells at level1
    1152           0 :     logical, dimension(:, :), allocatable :: mask1
    1153             : 
    1154             :     ! mask of valid sm cells
    1155           0 :     logical, dimension(:, :), allocatable :: mask_sm
    1156             : 
    1157             :     ! mask for valid sm catchment avg time steps
    1158           0 :     logical, dimension(:), allocatable :: mask_times
    1159             : 
    1160             : 
    1161           0 :     allocate(smOptiSim(domainMeta%nDomains))
    1162           0 :     call eval(parameterset, smOptiSim = smOptiSim)
    1163             : 
    1164             :     ! initialize some variables
    1165           0 :     objective_sm_pd = 0.0_dp
    1166             : 
    1167             :     ! loop over domain - for applying power law later on
    1168           0 :     do iDomain = 1, domainMeta%nDomains
    1169             : 
    1170             :       ! get domain information
    1171           0 :       mask1 = level1(iDomain)%mask
    1172           0 :       ncols1 = level1(iDomain)%ncols
    1173           0 :       nrows1 = level1(iDomain)%nrows
    1174             : 
    1175             :       ! allocate
    1176           0 :       allocate(mask_times    (size(smOptiSim(iDomain)%dataSim, dim = 2)))
    1177           0 :       allocate(pd_time_series(size(smOptiSim(iDomain)%dataSim, dim = 2)))
    1178           0 :       allocate(mat1   (nrows1, ncols1))
    1179           0 :       allocate(mat2   (nrows1, ncols1))
    1180           0 :       allocate(mask_sm(nrows1, ncols1))
    1181             : 
    1182             :       ! initalize
    1183           0 :       mask_times = .FALSE.
    1184           0 :       pd_time_series = 0.0_dp
    1185             : 
    1186             :       ! calculate pattern similarity criterion
    1187           0 :       do iTime = 1, size(smOptiSim(iDomain)%dataSim, dim = 2)
    1188           0 :         mat1 = unpack(L1_smObs(iDomain)%dataObs(:, iTime), mask1, nodata_dp)
    1189           0 :         mat2 = unpack(smOptiSim(iDomain)%dataSim(:, iTime), mask1, nodata_dp)
    1190           0 :         mask_sm = unpack(L1_smObs(iDomain)%maskObs(:, iTime), mask1, .FALSE.)
    1191           0 :         pd_time_series = PD(mat1, mat2, mask = mask_sm, valid = mask_times(itime))
    1192             :       end do
    1193             : 
    1194           0 :       if (count(mask_times) > 0_i4) then
    1195             :         ! calculate avergae PD over all domains with power law -domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    1196             :         objective_sm_pd = objective_sm_pd + &
    1197             :                 ((1.0_dp - sum(pd_time_series, mask = mask_times) / real(count(mask_times), dp)) / &
    1198           0 :                                                     real(domainMeta%overallNumberOfDomains, dp))**6
    1199             :       else
    1200           0 :         call error_message('***ERROR: mo_objective_funtion: objective_sm_pd: No soil moisture observations available!')
    1201             :       end if
    1202             : 
    1203             :       ! deallocate
    1204           0 :       deallocate(mask_times)
    1205           0 :       deallocate(pd_time_series)
    1206           0 :       deallocate(mat1)
    1207           0 :       deallocate(mat2)
    1208           0 :       deallocate(mask_sm)
    1209           0 :       call smOptiSim(iDomain)%destroy()
    1210             :     end do
    1211           0 :     deallocate(smOptiSim)
    1212             : 
    1213             : #ifndef MPI
    1214           0 :     objective_sm_pd = objective_sm_pd**onesixth
    1215             : 
    1216           0 :     call message('    objective_sm_pd = ', num2str(objective_sm_pd, '(F9.5)'))
    1217             : #endif
    1218             : 
    1219           0 :   END FUNCTION objective_sm_pd
    1220             : 
    1221             :   ! ------------------------------------------------------------------
    1222             : 
    1223             :   !    NAME
    1224             :   !        objective_sm_sse_standard_score
    1225             : 
    1226             :   !    PURPOSE
    1227             :   !>       \brief Objective function for soil moisture.
    1228             : 
    1229             :   !>       \details The objective function only depends on a parameter vector.
    1230             :   !>       The model will be called with that parameter vector and
    1231             :   !>       the model output is subsequently compared to observed data.
    1232             : 
    1233             :   !>       Therefore the sum of squared errors (SSE) of the standard score of observed and
    1234             :   !>       modeled soil moisture is calculated. The standard score or normalization (anomaly)
    1235             :   !>       make the objctive function bias insensitive and basically the dynamics of the soil moisture
    1236             :   !>       is tried to capture by this obejective function.
    1237             :   !>       \f[ phi_i = \sum_{j=1}^K \{ standard\_score( SM_{obs}(j) )- standard\_score(SM_{sim}(j)) \}^2 \f]
    1238             :   !>       where
    1239             :   !>       \f$  standard\_score \f$ = standard score function,
    1240             :   !>       \f$ SM_{obs} \f$  = observed soil moisture,
    1241             :   !>       \f$ SM_{sim}  \f$ = simulated soil moisture.
    1242             :   !>       \f$ K  \f$ = valid elements in study domain.
    1243             :   !>       Finally, the overall objective function value \f$ OF \f$ is estimated based on the power-6
    1244             :   !>       norm to combine the \f$ \phi_{i} \f$ from all domains \f$ N \f$.
    1245             :   !>       \f[ OF = \sqrt[6]{\sum(\phi_{i}/N)^6 }.  \f]
    1246             :   !>       The observed data L1_sm, L1_sm_mask are global in this module.
    1247             : 
    1248             :   !    INTENT(IN)
    1249             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    1250             :   !>       \param[in] "procedure(eval_interface) :: eval"
    1251             : 
    1252             :   !    RETURN
    1253             :   !>       \return real(dp) :: objective_sm_sse_standard_score — objective function value
    1254             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    1255             : 
    1256             :   !    HISTORY
    1257             :   !>       \authors Matthias Zink
    1258             : 
    1259             :   !>       \date March 2015
    1260             : 
    1261             :   ! Modifications:
    1262             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    1263             : 
    1264           0 :   FUNCTION objective_sm_sse_standard_score(parameterset, eval)
    1265             : 
    1266           0 :     use mo_optimization_types, only : optidata_sim
    1267             :     use mo_common_variables, only : level1, domainMeta
    1268             :     use mo_errormeasures, only : SSE
    1269             :     use mo_global_variables, only : L1_smObs
    1270             :     use mo_standard_score, only : standard_score
    1271             :     use mo_string_utils, only : num2str
    1272             : 
    1273             :     implicit none
    1274             : 
    1275             :     real(dp), dimension(:), intent(in) :: parameterset
    1276             : 
    1277             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1278             : 
    1279             :     real(dp) :: objective_sm_sse_standard_score
    1280             : 
    1281             :     ! domain loop counter
    1282             :     integer(i4) :: iDomain
    1283             : 
    1284             :     ! cell loop counter
    1285             :     integer(i4) :: iCell
    1286             : 
    1287             :     ! ncells1 of level 1
    1288             :     integer(i4) :: ncells1
    1289             : 
    1290             :     ! number of invalid cells in catchment
    1291           0 :     real(dp) :: invalid_cells
    1292             : 
    1293             :     ! domains wise objectives
    1294           0 :     real(dp) :: objective_sm_sse_standard_score_domain
    1295             : 
    1296             : #ifndef MPI
    1297             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    1298             : #endif
    1299             : 
    1300             :     ! simulated soil moisture
    1301           0 :     type(optidata_sim), dimension(:), allocatable :: smOptiSim
    1302             : 
    1303             : 
    1304           0 :     call eval(parameterset, smOptiSim = smOptiSim)
    1305             : 
    1306             :     ! initialize some variables
    1307           0 :     objective_sm_sse_standard_score = 0.0_dp
    1308             : 
    1309             :     ! loop over domain - for applying power law later on
    1310           0 :     do iDomain = 1, domainMeta%nDomains
    1311             : 
    1312             :       ! init
    1313           0 :       objective_sm_sse_standard_score_domain = 0.0_dp
    1314             :       ! get domain information
    1315           0 :       nCells1 = level1(iDomain)%nCells
    1316             : 
    1317           0 :       invalid_cells = 0.0_dp
    1318             :       ! standard_score signal is calculated on individual grid cells
    1319           0 :       do iCell = 1, size(L1_smObs(iDomain)%maskObs(:, :), dim = 1)
    1320             : 
    1321             :         ! check for enough data points in time for statistical calculations (e.g. mean, stddev)
    1322           0 :         if (count(L1_smObs(iDomain)%maskObs(iCell, :)) .LE. (0.10_dp * real(size(L1_smObs(iDomain)%dataObs, dim = 2), dp))) then
    1323           0 :           invalid_cells = invalid_cells + 1.0_dp
    1324           0 :           cycle
    1325             :         end if
    1326             :         objective_sm_sse_standard_score_domain = objective_sm_sse_standard_score_domain + &
    1327             :                 SSE(standard_score(L1_smObs(iDomain)%dataObs(iCell, :), mask = L1_smObs(iDomain)%maskObs(iCell, :)), &
    1328           0 :                         standard_score(smOptiSim(iDomain)%dataSim(iCell, :), mask = L1_smObs(iDomain)%maskObs(iCell, :)), &
    1329           0 :                                                           mask = L1_smObs(iDomain)%maskObs(iCell, :))
    1330             : 
    1331             :       end do
    1332             : 
    1333             :       ! user information about invalid cells
    1334           0 :       if (invalid_cells .GT. 0.5_dp) then
    1335           0 :         call message('   WARNING: objective_sm: Detected invalid cells in study area (.LT. 10 valid data points).')
    1336             :         call message('                          Fraction of invalid cells: ', &
    1337           0 :                 num2str(invalid_cells / real(nCells1, dp), '(F4.2)'))
    1338             :       end if
    1339             : 
    1340             :       ! calculate average soil moisture correlation over all domains with power law
    1341             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    1342             :       objective_sm_sse_standard_score = objective_sm_sse_standard_score + &
    1343           0 :               (objective_sm_sse_standard_score_domain / real(domainMeta%overallNumberOfDomains, dp))**6
    1344             :     end do
    1345             : 
    1346             : #ifndef MPI
    1347           0 :     objective_sm_sse_standard_score = objective_sm_sse_standard_score**onesixth
    1348             : 
    1349           0 :     call message('    objective_sm_sse_standard_score = ', num2str(objective_sm_sse_standard_score, '(E12.5)'))
    1350             : #endif
    1351             : 
    1352           0 :   END FUNCTION objective_sm_sse_standard_score
    1353             : 
    1354             : 
    1355             :   ! -----------------------------------------------------------------
    1356             : 
    1357             :   !    NAME
    1358             :   !        objective_kge_q_rmse_tws
    1359             : 
    1360             :   !    PURPOSE
    1361             :   !>       \brief Objective function of KGE for runoff and RMSE for domain_avg TWS (standarized scores)
    1362             : 
    1363             :   !>       \details Objective function of KGE for runoff and RMSE for domain_avg TWS (standarized scores)
    1364             : 
    1365             :   !    INTENT(IN)
    1366             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    1367             :   !>       \param[in] "procedure(eval_interface) :: eval"
    1368             : 
    1369             :   !    RETURN
    1370             :   !>       \return real(dp) :: objective_kge_q_rmse_tws — objective function value
    1371             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    1372             : 
    1373             :   !    HISTORY
    1374             :   !>       \authors Oldrich Rakovec, Rohini Kumar
    1375             : 
    1376             :   !>       \date Oct. 2015
    1377             : 
    1378             :   ! Modifications:
    1379             :   ! Stephan Thober Oct 2015 - moved tws optimization from mo_mrm_objective_function_runoff here
    1380             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    1381             :   ! Maren Kaluza Oct 2019 - changed averaging function for tws, this will not produce the same output as before
    1382             : 
    1383          12 :   FUNCTION objective_kge_q_rmse_tws(parameterset, eval)
    1384             : 
    1385           0 :     use mo_optimization_types, only : optidata_sim
    1386             :     use mo_common_constants, only : eps_dp, nodata_dp
    1387             :     use mo_common_mhm_mrm_variables, only : evalPer
    1388             :     use mo_common_variables, only : domainMeta
    1389             :     use mo_global_variables, only : L1_twsaObs
    1390             :     use mo_errormeasures, only : rmse
    1391             :     use mo_julian, only : caldat
    1392             :     use mo_moment, only : mean
    1393             :     use mo_standard_score, only : classified_standard_score
    1394             :     use mo_string_utils, only : num2str
    1395             :     use mo_temporal_aggregation, only : day2mon_average
    1396             :     use mo_errormeasures, only : kge
    1397             :     use mo_mrm_objective_function_runoff, only : extract_runoff
    1398             : 
    1399             :     implicit none
    1400             : 
    1401             :     real(dp), dimension(:), intent(in) :: parameterset
    1402             : 
    1403             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1404             : 
    1405             :     real(dp) :: objective_kge_q_rmse_tws
    1406             : 
    1407             :     ! modelled runoff for a given parameter set
    1408             :     ! dim1=nTimeSteps, dim2=nGauges
    1409           6 :     real(dp), allocatable, dimension(:, :) :: runoff
    1410             : 
    1411             :     !> simulated tws
    1412           6 :     type(optidata_sim), dimension(:), allocatable :: twsOptiSim
    1413             : 
    1414             :     ! domain counter, month counters
    1415             :     integer(i4) :: domainID, iDomain, pp, mmm
    1416             : 
    1417             :     integer(i4) :: year, month, day
    1418             : 
    1419          18 :     real(dp), dimension(domainMeta%nDomains) :: initTime
    1420             : 
    1421             :     ! simulated tws
    1422           6 :     real(dp), dimension(:), allocatable :: tws_catch_avg_domain
    1423             : 
    1424             :     ! measured tws
    1425           6 :     real(dp), dimension(:), allocatable :: tws_opti_catch_avg_domain
    1426             : 
    1427             :     ! mask for measured tws
    1428           6 :     logical, dimension(:), allocatable :: tws_obs_mask
    1429             : 
    1430             :     ! total number of months
    1431             :     integer(i4) :: nMonths
    1432             : 
    1433             :     ! vector with months' classes
    1434           6 :     integer(i4), dimension(:), allocatable :: month_classes
    1435             : 
    1436             :     ! monthly values anomaly time series
    1437           6 :     real(dp), DIMENSION(:), allocatable :: tws_sim_m_anom, tws_obs_m_anom
    1438             : 
    1439             :     ! rmse_tws(domainMeta%nDomains)
    1440           6 :     real(dp), dimension(:), allocatable :: rmse_tws
    1441             : 
    1442             :     ! obj. functions
    1443           6 :     real(dp) :: rmse_tws_avg, kge_q_avg
    1444             : 
    1445             :     integer(i4) :: nGaugesTotal
    1446             : 
    1447             :     ! aggregated simulated runoff
    1448           6 :     real(dp), dimension(:), allocatable :: runoff_agg
    1449             : 
    1450             :     ! measured runoff
    1451           6 :     real(dp), dimension(:), allocatable :: runoff_obs
    1452             : 
    1453             :     ! mask for measured runoff
    1454           6 :     logical, dimension(:), allocatable :: runoff_obs_mask
    1455             : 
    1456             :     ! kge_q(nGaugesTotal)
    1457           6 :     real(dp), dimension(:), allocatable :: kge_q
    1458             : 
    1459             :     ! gauges counter
    1460             :     integer(i4) :: gg
    1461             : 
    1462             :     ! obtain hourly values of runoff and tws:
    1463          24 :     allocate(twsOptiSim(domainMeta%nDomains))
    1464           6 :     call eval(parameterset, runoff = runoff, twsOptiSim = twsOptiSim)
    1465             : 
    1466             :     !--------------------------------------------
    1467             :     !! TWS
    1468             :     !--------------------------------------------
    1469             : 
    1470             :     ! allocate
    1471          18 :     allocate(rmse_tws(domainMeta%nDomains))
    1472          12 :     rmse_tws(:) = nodata_dp
    1473             : 
    1474          12 :     do iDomain = 1, domainMeta%nDomains
    1475           6 :       if (.not. (L1_twsaObs(iDomain)%timeStepInput == -2)) then
    1476           0 :         call message('objective_kge_q_rmse_tws: current implementation of this subroutine only allows monthly timesteps')
    1477             :       end if
    1478           6 :       domainID = domainMeta%indices(iDomain)
    1479             : 
    1480             :       ! extract tws the same way as runoff using mrm
    1481             :       ! Note that with the change from tws(iDomain, tt) to tws(tt, :) this
    1482             :       ! will not work like before and also does maybe not make sense
    1483           6 :       call create_domain_avg_tws(iDomain, twsOptiSim, tws_catch_avg_domain, tws_opti_catch_avg_domain, tws_obs_mask)
    1484             : 
    1485             :       ! check for potentially 2 years of data
    1486         150 :       if (count(tws_obs_mask) .lt.  12 * 2) then
    1487             :         call message('objective_kge_q_rmse_tws: Length of TWS data of domain ', trim(adjustl(num2str(domainID))), &
    1488           0 :                 ' less than 2 years: this is not recommended')
    1489             :       end if
    1490             : 
    1491             :       ! get initial time of the evaluation period
    1492           6 :       initTime(iDomain) = real(evalPer(iDomain)%julStart, dp)
    1493             : 
    1494             :       ! get calendar days, months, year
    1495           6 :       call caldat(int(initTime(iDomain)), yy = year, mm = month, dd = day)
    1496             : 
    1497           6 :       nMonths = size(tws_obs_mask)
    1498             : 
    1499          18 :       allocate (month_classes(nMonths))
    1500          18 :       allocate (tws_obs_m_anom(nMonths))
    1501          12 :       allocate (tws_sim_m_anom(nMonths))
    1502             : 
    1503         150 :       month_classes(:) = 0
    1504         150 :       tws_obs_m_anom(:) = nodata_dp
    1505         150 :       tws_sim_m_anom(:) = nodata_dp
    1506             : 
    1507             :       ! define months' classes
    1508           6 :       mmm = month
    1509         150 :       do pp = 1, nMonths
    1510         144 :         month_classes(pp) = mmm
    1511         150 :         if (mmm .LT. 12) then
    1512         132 :           mmm = mmm + 1
    1513             :         else
    1514             :           mmm = 1
    1515             :         end if
    1516             :       end do
    1517             : 
    1518             :       ! calculate standard score
    1519           6 :       tws_obs_m_anom = classified_standard_score(tws_opti_catch_avg_domain, month_classes, mask = tws_obs_mask)
    1520           6 :       tws_sim_m_anom = classified_standard_score(tws_catch_avg_domain,      month_classes, mask = tws_obs_mask)
    1521           6 :       rmse_tws(iDomain) = rmse(tws_sim_m_anom, tws_obs_m_anom, mask = tws_obs_mask)
    1522             : 
    1523           6 :       deallocate (month_classes)
    1524           6 :       deallocate (tws_sim_m_anom)
    1525           6 :       deallocate (tws_obs_m_anom)
    1526           6 :       deallocate (tws_catch_avg_domain, tws_opti_catch_avg_domain, tws_obs_mask)
    1527             : 
    1528          18 :       call twsOptiSim(iDomain)%destroy()
    1529             :     end do
    1530             : 
    1531             :     rmse_tws_avg = sum(rmse_tws(:), abs(rmse_tws - nodata_dp) .gt. eps_dp) / &
    1532          24 :             real(count(abs(rmse_tws - nodata_dp) .gt. eps_dp), dp)
    1533           6 :     deallocate(rmse_tws)
    1534          12 :     deallocate(twsOptiSim)
    1535             : 
    1536             :     !--------------------------------------------
    1537             :     !! RUNOFF
    1538             :     !--------------------------------------------
    1539           6 :     kge_q_avg = 0_dp
    1540           6 :     nGaugesTotal = size(runoff, dim = 2)
    1541          18 :     allocate(kge_q(nGaugesTotal))
    1542          12 :     kge_q(:) = nodata_dp
    1543             : 
    1544          12 :     do gg = 1, nGaugesTotal
    1545             : 
    1546             :       ! extract runoff
    1547           6 :       call extract_runoff(gg, runoff, runoff_agg, runoff_obs, runoff_obs_mask)
    1548             : 
    1549             :       ! check for potentially 2 years of data
    1550        4386 :       pp = count(runoff_agg .ge. 0.0_dp)
    1551           6 :       if (pp .lt.  365 * 2) then
    1552             :         call message('objective_kge_q_rmse_tws: The simulation at gauge ', trim(adjustl(num2str(gg))), &
    1553           0 :         ' is not long enough. Please provide at least 730 days of data.')
    1554             :       end if
    1555             :       ! calculate KGE for each domain:
    1556           6 :       kge_q(gg) = kge(runoff_obs, runoff_agg, mask = runoff_obs_mask)
    1557          18 :       deallocate (runoff_agg, runoff_obs, runoff_obs_mask)
    1558             : 
    1559             :     end do
    1560             : 
    1561             :     ! calculate average KGE value for runoff
    1562             :     kge_q_avg = sum(kge_q(:), abs(kge_q - nodata_dp) .gt. eps_dp) / &
    1563          24 :             real(count(abs(kge_q - nodata_dp) .gt. eps_dp), dp)
    1564           6 :     deallocate(kge_q)
    1565             : 
    1566             :     !
    1567           6 :     objective_kge_q_rmse_tws = rmse_tws_avg * (1._dp - kge_q_avg)
    1568             : 
    1569           6 :     call message('    objective_kge_q_rmse_tws = ', num2str(objective_kge_q_rmse_tws, '(F9.5)'))
    1570             : 
    1571           6 :   END FUNCTION objective_kge_q_rmse_tws
    1572             : 
    1573             :   ! ------------------------------------------------------------------
    1574             : 
    1575             :   !    NAME
    1576             :   !        objective_neutrons_kge_catchment_avg
    1577             : 
    1578             :   !    PURPOSE
    1579             :   !>       \brief Objective function for neutrons.
    1580             : 
    1581             :   !>       \details The objective function only depends on a parameter vector.
    1582             :   !>       The model will be called with that parameter vector and
    1583             :   !>       the model output is subsequently compared to observed data.
    1584             : 
    1585             :   !>       Therefore, the Kling-Gupta model efficiency \f$ KGE \f$ of the catchment average
    1586             :   !>       neutrons (N) is calculated
    1587             :   !>       \f[ KGE = 1.0 - \sqrt{( (1-r)^2 + (1-\alpha)^2 + (1-\beta)^2 )} \f]
    1588             :   !>       where
    1589             :   !>       \f$ r \f$ = Pearson product-moment CORRELATION coefficient
    1590             :   !>       \f$ \alpha \f$ = ratio of simulated mean to observed mean SM
    1591             :   !>       \f$ \beta  \f$ = ratio of similated standard deviation to observed standard deviation
    1592             :   !>       is calculated and the objective function for a given domain \f$ i \f$ is
    1593             :   !>       \f[ \phi_{i} = 1.0 - KGE_{i} \f]
    1594             :   !>       \f$ \phi_{i} \f$ is the objective since we always apply minimization methods.
    1595             :   !>       The minimal value of \f$ \phi_{i} \f$ is 0 for the optimal KGE of 1.0.
    1596             : 
    1597             :   !>       Finally, the overall objective function value \f$ OF \f$ is estimated based on the power-6
    1598             :   !>       norm to combine the \f$ \phi_{i} \f$ from all domains \f$ N \f$.
    1599             :   !>       \f[ OF = \sqrt[6]{\sum((1.0 - KGE_{i})/N)^6 }.  \f]
    1600             :   !>       The observed data L1_neutronsdata, L1_neutronsdata_mask are global in this module.
    1601             : 
    1602             :   !    INTENT(IN)
    1603             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    1604             :   !>       \param[in] "procedure(eval_interface) :: eval"
    1605             : 
    1606             :   !    RETURN
    1607             :   !>       \return real(dp) :: objective_neutrons_kge_catchment_avg — objective function value
    1608             :   !>       (which will be e.g. minimized by an optimization routine)
    1609             : 
    1610             :   !    HISTORY
    1611             :   !>       \authors Martin Schroen
    1612             : 
    1613             :   !>       \date Jun 2015
    1614             : 
    1615             :   ! Modifications:
    1616             :   ! Maren Kaluza Mar 2018 - changed format string to '(I10)'
    1617             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    1618             : 
    1619          12 :   FUNCTION objective_neutrons_kge_catchment_avg(parameterset, eval)
    1620             : 
    1621           6 :     use mo_optimization_types, only : optidata_sim
    1622             :     use mo_common_constants, only : nodata_dp
    1623             :     use mo_common_variables, only : domainMeta
    1624             :     use mo_errormeasures, only : KGE
    1625             :     use mo_global_variables, only : L1_neutronsObs
    1626             :     use mo_moment, only : average
    1627             :     use mo_string_utils, only : num2str
    1628             : 
    1629             :     implicit none
    1630             : 
    1631             :     real(dp), dimension(:), intent(in) :: parameterset
    1632             : 
    1633             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1634             : 
    1635             :     real(dp) :: objective_neutrons_kge_catchment_avg
    1636             : 
    1637             :     ! domain loop counter
    1638             :     integer(i4) :: iDomain
    1639             : 
    1640             :     ! time loop counter
    1641             :     integer(i4) :: iTime
    1642             : 
    1643             :     ! for sixth root
    1644             : #ifndef MPI
    1645             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    1646             : #endif
    1647             : 
    1648             :     ! spatial average of observed neutrons
    1649           6 :     real(dp), dimension(:), allocatable :: neutrons_catch_avg_domain
    1650             : 
    1651             :     ! spatial avergae of modeled  neutrons
    1652           6 :     real(dp), dimension(:), allocatable :: neutrons_opti_catch_avg_domain
    1653             : 
    1654             :     ! simulated neutrons
    1655           6 :     type(optidata_sim), dimension(:), allocatable :: neutronsOptiSim
    1656             : 
    1657             :     ! mask for valid neutrons catchment avg time steps
    1658           6 :     logical, dimension(:), allocatable :: mask_times
    1659             : 
    1660             : 
    1661          24 :     allocate(neutronsOptiSim(domainMeta%nDomains))
    1662           6 :     call eval(parameterset, neutronsOptiSim = neutronsOptiSim)
    1663             : 
    1664             :     ! initialize some variables
    1665           6 :     objective_neutrons_kge_catchment_avg = 0.0_dp
    1666             : 
    1667             :     ! loop over domain - for applying power law later on
    1668          12 :     do iDomain = 1, domainMeta%nDomains
    1669             : 
    1670             :       ! allocate
    1671          18 :       allocate(mask_times                    (size(neutronsOptiSim(iDomain)%dataSim, dim = 2)))
    1672          18 :       allocate(neutrons_catch_avg_domain     (size(neutronsOptiSim(iDomain)%dataSim, dim = 2)))
    1673          12 :       allocate(neutrons_opti_catch_avg_domain(size(neutronsOptiSim(iDomain)%dataSim, dim = 2)))
    1674             : 
    1675             :       ! initalize
    1676        2196 :       mask_times = .TRUE.
    1677        2196 :       neutrons_catch_avg_domain = nodata_dp
    1678        2196 :       neutrons_opti_catch_avg_domain = nodata_dp
    1679             : 
    1680             :       ! calculate catchment average soil moisture
    1681        2196 :       do iTime = 1, size(neutronsOptiSim(iDomain)%dataSim, dim = 2)
    1682             : 
    1683             :         ! check for enough data points in time for correlation
    1684        2190 :         if (all(.NOT. L1_neutronsObs(iDomain)%maskObs(:, iTime))) then
    1685           0 :           call message('WARNING: neutrons data at time ', num2str(iTime, '(I10)'), ' is empty.')
    1686             :           !call message('WARNING: objective_neutrons_kge_catchment_avg: ignored current time step since less than')
    1687             :           !call message('         10 valid cells available in soil moisture observation')
    1688           0 :           mask_times(iTime) = .FALSE.
    1689           0 :           cycle
    1690             :         end if
    1691           0 :         neutrons_catch_avg_domain(iTime) = average(L1_neutronsObs(iDomain)%dataObs(:, iTime), &
    1692        2190 :                                             mask = L1_neutronsObs(iDomain)%maskObs(:, iTime))
    1693           0 :         neutrons_opti_catch_avg_domain(iTime) = average(neutronsOptiSim(iDomain)%dataSim(:, iTime), &
    1694        2196 :                                          mask = L1_neutronsObs(iDomain)%maskObs(:, iTime))
    1695             :       end do
    1696             : 
    1697             :       ! calculate average neutrons KGE over all domains with power law
    1698             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    1699             :       objective_neutrons_kge_catchment_avg = objective_neutrons_kge_catchment_avg + &
    1700             :         ((1.0_dp - KGE(neutrons_catch_avg_domain, neutrons_opti_catch_avg_domain, mask = mask_times)) / &
    1701           6 :                                                                       real(domainMeta%overallNumberOfDomains, dp))**6
    1702             : 
    1703             :       ! deallocate
    1704           6 :       deallocate(mask_times)
    1705           6 :       deallocate(neutrons_catch_avg_domain)
    1706           6 :       deallocate(neutrons_opti_catch_avg_domain)
    1707             : 
    1708          12 :       call neutronsOptiSim(iDomain)%destroy()
    1709             :     end do
    1710          12 :     deallocate(neutronsOptiSim)
    1711             : 
    1712             : #ifndef MPI
    1713           6 :     objective_neutrons_kge_catchment_avg = objective_neutrons_kge_catchment_avg**onesixth
    1714             : 
    1715           6 :     call message('    objective_neutrons_kge_catchment_avg = ', num2str(objective_neutrons_kge_catchment_avg, '(F9.5)'))
    1716             : #endif
    1717             : 
    1718           6 :   END FUNCTION objective_neutrons_kge_catchment_avg
    1719             : 
    1720             :   ! ------------------------------------------------------------------
    1721             : 
    1722             :   !    NAME
    1723             :   !        objective_et_kge_catchment_avg
    1724             : 
    1725             :   !    PURPOSE
    1726             :   !>       \brief Objective function for evpotranspirstion (et).
    1727             : 
    1728             :   !>       \details The objective function only depends on a parameter vector.
    1729             :   !>       The model will be called with that parameter vector and
    1730             :   !>       the model output is subsequently compared to observed data.
    1731             : 
    1732             :   !>       Therefore, the Kling-Gupta model efficiency \f$ KGE \f$ of the catchment average
    1733             :   !>       evapotranspiration (et) is calculated
    1734             :   !>       \f[ KGE = 1.0 - \sqrt{( (1-r)^2 + (1-\alpha)^2 + (1-\beta)^2 )} \f]
    1735             :   !>       where
    1736             :   !>       \f$ r \f$ = Pearson product-moment correlation coefficient
    1737             :   !>       \f$ \alpha \f$ = ratio of simulated mean to observed mean SM
    1738             :   !>       \f$ \beta  \f$ = ratio of similated standard deviation to observed standard deviation
    1739             :   !>       is calculated and the objective function for a given domain \f$ i \f$ is
    1740             :   !>       \f[ \phi_{i} = 1.0 - KGE_{i} \f]
    1741             :   !>       \f$ \phi_{i} \f$ is the objective since we always apply minimization methods.
    1742             :   !>       The minimal value of \f$ \phi_{i} \f$ is 0 for the optimal KGE of 1.0.
    1743             : 
    1744             :   !>       Finally, the overall objective function value \f$ OF \f$ is estimated based on the power-6
    1745             :   !>       norm to combine the \f$ \phi_{i} \f$ from all domains \f$ N \f$.
    1746             :   !>       \f[ OF = \sqrt[6]{\sum((1.0 - KGE_{i})/N)^6 }.  \f]
    1747             :   !>       The observed data L1_et, L1_et_mask are global in this module.
    1748             : 
    1749             :   !    INTENT(IN)
    1750             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    1751             :   !>       \param[in] "procedure(eval_interface) :: eval"
    1752             : 
    1753             :   !    RETURN
    1754             :   !>       \return real(dp) :: objective_et_kge_catchment_avg — objective function value
    1755             :   !>       (which will be e.g. minimized by an optimization routine)
    1756             : 
    1757             :   !    HISTORY
    1758             :   !>       \authors Johannes Brenner
    1759             : 
    1760             :   !>       \date Feb 2017
    1761             : 
    1762             :   ! Modifications:
    1763             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    1764             : 
    1765           0 :   FUNCTION objective_et_kge_catchment_avg(parameterset, eval)
    1766             : 
    1767           6 :     use mo_optimization_types, only : optidata_sim
    1768             :     use mo_common_constants, only : nodata_dp
    1769             :     use mo_common_variables, only : domainMeta
    1770             :     use mo_errormeasures, only : KGE
    1771             :     use mo_moment, only : average
    1772             :     use mo_string_utils, only : num2str
    1773             : 
    1774             :     implicit none
    1775             : 
    1776             :     real(dp), dimension(:), intent(in) :: parameterset
    1777             : 
    1778             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1779             : 
    1780             :     real(dp) :: objective_et_kge_catchment_avg
    1781             : 
    1782             :     ! domain loop counter
    1783             :     integer(i4) ::iDomain
    1784             : 
    1785             :     ! for sixth root
    1786             : #ifndef MPI
    1787             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    1788             : #endif
    1789             : 
    1790             :     ! spatial average of observed et
    1791           0 :     real(dp), dimension(:), allocatable :: et_catch_avg_domain
    1792             : 
    1793             :     ! spatial avergae of modeled  et
    1794           0 :     real(dp), dimension(:), allocatable :: et_opti_catch_avg_domain
    1795             : 
    1796             :     !> simulated et
    1797           0 :     type(optidata_sim), dimension(:), allocatable :: etOptiSim
    1798             : 
    1799             :     ! mask for valid et catchment avg time steps
    1800           0 :     logical, dimension(:), allocatable :: mask_times
    1801             : 
    1802             : 
    1803           0 :     call eval(parameterset, etOptiSim = etOptiSim)
    1804             : 
    1805             :     ! initialize some variables
    1806           0 :     objective_et_kge_catchment_avg = 0.0_dp
    1807             : 
    1808             :     ! loop over domain - for applying power law later on
    1809           0 :     allocate(etOptiSim(domainMeta%nDomains))
    1810           0 :     do iDomain = 1, domainMeta%nDomains
    1811             : 
    1812             :       ! create et array input
    1813             :       call create_domain_avg_et(iDomain, etOptiSim, et_catch_avg_domain, &
    1814           0 :                                            et_opti_catch_avg_domain, mask_times)
    1815             :       ! calculate average ET KGE over all domains with power law
    1816             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    1817             : 
    1818             :       objective_et_kge_catchment_avg = objective_et_kge_catchment_avg + &
    1819             :               ((1.0_dp - KGE(et_catch_avg_domain, et_opti_catch_avg_domain, mask = mask_times)) / &
    1820           0 :                                         real(domainMeta%overallNumberOfDomains, dp))**6
    1821             : 
    1822             :       ! deallocate
    1823           0 :       deallocate(mask_times)
    1824           0 :       deallocate(et_catch_avg_domain)
    1825           0 :       deallocate(et_opti_catch_avg_domain)
    1826           0 :       call etOptiSim(iDomain)%destroy()
    1827             :     end do
    1828           0 :     deallocate(etOptiSim)
    1829             : 
    1830             : #ifndef MPI
    1831           0 :     objective_et_kge_catchment_avg = objective_et_kge_catchment_avg**onesixth
    1832             : 
    1833           0 :     call message('    objective_et_kge_catchment_avg = ', num2str(objective_et_kge_catchment_avg, '(F9.5)'))
    1834             : #endif
    1835             : 
    1836           0 :   END FUNCTION objective_et_kge_catchment_avg
    1837             : 
    1838             :   ! -----------------------------------------------------------------
    1839             : 
    1840             :   !    NAME
    1841             :   !        objective_kge_q_sm_corr
    1842             : 
    1843             :   !    PURPOSE
    1844             :   !>       \brief Objective function of KGE for runoff and correlation for SM
    1845             : 
    1846             :   !>       \details Objective function of KGE for runoff and SSE for soil moisture (standarized scores).
    1847             :   !>       Further details can be found in the documentation of objective functions
    1848             :   !>       '14 - objective_multiple_gauges_kge_power6' and '13 - objective_sm_corr'.
    1849             : 
    1850             :   !    INTENT(IN)
    1851             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    1852             :   !>       \param[in] "procedure(eval_interface) :: eval"
    1853             : 
    1854             :   !    RETURN
    1855             :   !>       \return real(dp) :: objective_kge_q_sse_sm — objective function value
    1856             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    1857             : 
    1858             :   !    HISTORY
    1859             :   !>       \authors Matthias Zink
    1860             : 
    1861             :   !>       \date Mar. 2017
    1862             : 
    1863             :   ! Modifications:
    1864             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    1865             : 
    1866          12 :   FUNCTION objective_kge_q_sm_corr(parameterset, eval)
    1867             : 
    1868           0 :     use mo_optimization_types, only : optidata_sim
    1869             :     use mo_common_variables, only : level1, domainMeta
    1870             :     use mo_global_variables, only : L1_smObs
    1871             :     use mo_moment, only : correlation
    1872             :     use mo_string_utils, only : num2str
    1873             :     use mo_errormeasures, only : kge
    1874             :     use mo_mrm_objective_function_runoff, only : extract_runoff
    1875             : 
    1876             :     implicit none
    1877             : 
    1878             :     real(dp), dimension(:), intent(in) :: parameterset
    1879             : 
    1880             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1881             : 
    1882             :     real(dp) :: objective_kge_q_sm_corr
    1883             : 
    1884           6 :     real(dp) :: objective_sm
    1885             : 
    1886           6 :     real(dp) :: objective_kge
    1887             : 
    1888             :     ! number of invalid cells in catchment
    1889           6 :     real(dp) :: invalid_cells
    1890             : 
    1891             :     ! modelled runoff for a given parameter set
    1892             :     ! dim1=nTimeSteps, dim2=nGauges
    1893           6 :     real(dp), allocatable, dimension(:, :) :: runoff
    1894             : 
    1895             :     ! domain loop counter
    1896             :     integer(i4) :: iDomain
    1897             : 
    1898             :     ! cell loop counter
    1899             :     integer(i4) :: iCell
    1900             : 
    1901             :     ! ncells1 of level 1
    1902             :     integer(i4) :: ncells1
    1903             : 
    1904             :     ! domains wise objectives
    1905           6 :     real(dp) :: objective_sm_domain
    1906             : 
    1907             :     ! simulated soil moisture
    1908           6 :     type(optidata_sim), dimension(:), allocatable :: smOptiSim
    1909             : 
    1910             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    1911             : 
    1912             :     ! gauges counter
    1913             :     integer(i4) :: gg
    1914             : 
    1915             :     integer(i4) :: nGaugesTotal
    1916             : 
    1917             :     ! aggregated simulated runoff
    1918           6 :     real(dp), dimension(:), allocatable :: runoff_agg
    1919             : 
    1920             :     ! measured runoff
    1921           6 :     real(dp), dimension(:), allocatable :: runoff_obs
    1922             : 
    1923             :     ! mask for measured runoff
    1924           6 :     logical, dimension(:), allocatable :: runoff_obs_mask
    1925             : 
    1926             :     ! run mHM
    1927          24 :     allocate(smOptiSim(domainMeta%nDomains))
    1928           6 :     call eval(parameterset, runoff = runoff, smOptiSim = smOptiSim)
    1929             : 
    1930             :     ! -----------------------------
    1931             :     ! SOIL MOISTURE
    1932             :     ! -----------------------------
    1933             : 
    1934             :     ! initialize some variables
    1935           6 :     objective_sm = 0.0_dp
    1936             : 
    1937             :     ! loop over domain - for applying power law later on
    1938          12 :     do iDomain = 1, domainMeta%nDomains
    1939             : 
    1940             :       ! init
    1941           6 :       objective_sm_domain = 0.0_dp
    1942             :       ! get domain information
    1943           6 :       nCells1 = level1(iDomain)%nCells
    1944             : 
    1945             :       ! correlation signal is calculated on individual grid cells
    1946           6 :       invalid_cells = 0.0_dp
    1947         210 :       do iCell = 1, size(L1_smObs(iDomain)%maskObs(:, :), dim = 1)
    1948             : 
    1949             :         ! check for enough data points in time for statistical calculations (e.g. mean, stddev)
    1950        2652 :         if (count(L1_smObs(iDomain)%maskObs(iCell, :)) .LE. (0.10_dp * real(size(L1_smObs(iDomain)%dataObs, dim = 2), dp))) then
    1951           6 :           invalid_cells = invalid_cells + 1.0_dp
    1952           6 :           cycle
    1953             :         end if
    1954             : 
    1955             :         ! calculate ojective function
    1956             :         objective_sm_domain = objective_sm_domain + &
    1957           0 :                 correlation(L1_smObs(iDomain)%dataObs(iCell, :), smOptiSim(iDomain)%dataSim(iCell, :), &
    1958         204 :                                                            mask = L1_smObs(iDomain)%maskObs(iCell, :))
    1959             :       end do
    1960             : 
    1961             :       ! user information about invalid cells
    1962           6 :       if (invalid_cells .GT. 0.5_dp) then
    1963           6 :         call message('   WARNING: objective_sm: Detected invalid cells in study area (.LT. 10 valid data points).')
    1964             :         call message('                          Fraction of invalid cells: ', &
    1965           6 :                 num2str(invalid_cells / real(nCells1, dp), '(F4.2)'))
    1966             :       end if
    1967             : 
    1968             :       ! calculate average soil moisture objective over all domains with power law
    1969             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    1970             :       objective_sm = objective_sm + &
    1971           6 :               ((1.0_dp - objective_sm_domain / real(nCells1, dp)) / real(domainMeta%overallNumberOfDomains, dp))**6
    1972          12 :       call smOptiSim(iDomain)%destroy()
    1973             :     end do
    1974          12 :     deallocate(smOptiSim)
    1975             : 
    1976             :     ! compromise solution - sixth root
    1977           6 :     objective_sm = objective_sm**onesixth
    1978             : 
    1979             :     ! -----------------------------
    1980             :     ! RUNOFF
    1981             :     ! -----------------------------
    1982           6 :     objective_kge = 0.0_dp
    1983           6 :     nGaugesTotal = size(runoff, dim = 2)
    1984             : 
    1985          12 :     do gg = 1, nGaugesTotal
    1986             : 
    1987             :       ! extract runoff
    1988           6 :       call extract_runoff(gg, runoff, runoff_agg, runoff_obs, runoff_obs_mask)
    1989             : 
    1990             :       ! KGE
    1991             :       objective_kge = objective_kge + &
    1992          12 :               ((1.0_dp - kge(runoff_obs, runoff_agg, mask = runoff_obs_mask)) / real(nGaugesTotal, dp))**6
    1993             : 
    1994             :     end do
    1995             : 
    1996           6 :     deallocate(runoff_agg, runoff_obs, runoff_obs_mask)
    1997             : 
    1998             :     ! compromise solution - sixth root
    1999           6 :     objective_kge = objective_kge**onesixth
    2000             : 
    2001             :     ! equal weighted compromise objective functions for discharge and soilmoisture
    2002             :     ! ToDo: why do we use the sixth root of of objective_sm and objective_kge
    2003             :     ! only to take the power to 6 here again, when we never need the
    2004             :     ! intermediate results?
    2005             : #ifdef MPI
    2006             :     objective_kge_q_sm_corr = (objective_sm**6 + objective_kge**6)
    2007             : #else
    2008           6 :     objective_kge_q_sm_corr = (objective_sm**6 + objective_kge**6)**onesixth
    2009             : 
    2010           6 :     call message('    objective_kge_q_sm_corr = ', num2str(objective_kge_q_sm_corr, '(F9.5)'))
    2011             : #endif
    2012             :     !    print*, "1-SM 2-Q : ", 1.0_dp-objective_sm, 1.0_dp-objective_kge ! MZMZMZMZ
    2013             : 
    2014           6 :   END FUNCTION objective_kge_q_sm_corr
    2015             : 
    2016             : 
    2017             :   ! -----------------------------------------------------------------
    2018             : 
    2019             :   !    NAME
    2020             :   !        objective_kge_q_et
    2021             : 
    2022             :   !    PURPOSE
    2023             :   !>       \brief Objective function of KGE for runoff and KGE for ET
    2024             : 
    2025             :   !>       \details Objective function of KGE for runoff and KGE for ET.
    2026             :   !>       Further details can be found in the documentation of objective functions
    2027             :   !>       '14 - objective_multiple_gauges_kge_power6'.
    2028             : 
    2029             :   !    INTENT(IN)
    2030             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    2031             :   !>       \param[in] "procedure(eval_interface) :: eval"
    2032             : 
    2033             :   !    RETURN
    2034             :   !>       \return real(dp) :: objective_kge_q_et — objective function value
    2035             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    2036             : 
    2037             :   !    HISTORY
    2038             :   !>       \authors Johannes Brenner
    2039             : 
    2040             :   !>       \date July 2017
    2041             : 
    2042             :   ! Modifications:
    2043             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    2044             : 
    2045           0 :   FUNCTION objective_kge_q_et(parameterset, eval)
    2046             : 
    2047           6 :     use mo_optimization_types, only : optidata_sim
    2048             :     use mo_common_variables, only : level1, domainMeta
    2049             :     use mo_errormeasures, only : kge
    2050             :     use mo_global_variables, only : L1_etObs
    2051             :     use mo_string_utils, only : num2str
    2052             :     use mo_mrm_objective_function_runoff, only : extract_runoff
    2053             : 
    2054             :     implicit none
    2055             : 
    2056             :     real(dp), dimension(:), intent(in) :: parameterset
    2057             : 
    2058             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    2059             : 
    2060             :     real(dp) :: objective_kge_q_et
    2061             : 
    2062           0 :     real(dp) :: objective_et
    2063             : 
    2064           0 :     real(dp) :: objective_q
    2065             : 
    2066             :     ! number of invalid cells in catchment
    2067           0 :     real(dp) :: invalid_cells
    2068             : 
    2069             :     ! modelled runoff for a given parameter set
    2070             :     ! dim1=nTimeSteps, dim2=nGauges
    2071           0 :     real(dp), allocatable, dimension(:, :) :: runoff
    2072             : 
    2073             :     ! domain loop counter
    2074             :     integer(i4) :: iDomain
    2075             : 
    2076             :     ! cell loop counter
    2077             :     integer(i4) :: iCell
    2078             : 
    2079             :     ! ncells1 of level 1
    2080             :     integer(i4) :: nCells1
    2081             : 
    2082             :     ! domains wise objectives
    2083           0 :     real(dp) :: objective_et_domain
    2084             : 
    2085             :     !> simulated et
    2086           0 :     type(optidata_sim), dimension(:), allocatable :: etOptiSim
    2087             : 
    2088             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    2089             : 
    2090             :     ! gauges counter
    2091             :     integer(i4) :: gg
    2092             : 
    2093             :     integer(i4) :: nGaugesTotal
    2094             : 
    2095             :     ! aggregated simulated runoff
    2096           0 :     real(dp), dimension(:), allocatable :: runoff_agg
    2097             : 
    2098             :     ! measured runoff
    2099           0 :     real(dp), dimension(:), allocatable :: runoff_obs
    2100             : 
    2101             :     ! mask for measured runoff
    2102           0 :     logical, dimension(:), allocatable :: runoff_obs_mask
    2103             : 
    2104             :     ! run mHM
    2105           0 :     allocate(etOptiSim(domainMeta%nDomains))
    2106           0 :     call eval(parameterset, runoff = runoff, etOptiSim = etOptiSim)
    2107             : 
    2108             :     ! -----------------------------
    2109             :     ! EVAPOTRANSPIRATION
    2110             :     ! -----------------------------
    2111             : 
    2112             :     ! initialize some variables
    2113           0 :     objective_et = 0.0_dp
    2114             : 
    2115             :     ! loop over domain - for applying power law later on
    2116           0 :     do iDomain = 1, domainMeta%nDomains
    2117             : 
    2118             :       ! init
    2119           0 :       objective_et_domain = 0.0_dp
    2120             :       ! get domain information
    2121           0 :       nCells1 = level1(iDomain)%nCells
    2122             : 
    2123             :       ! correlation signal is calculated on individual grid cells
    2124           0 :       invalid_cells = 0.0_dp
    2125           0 :       do iCell = 1, size(L1_etObs(iDomain)%maskObs, dim = 1)
    2126             : 
    2127             :         ! check for enough data points in time for statistical calculations (e.g. mean, stddev)
    2128           0 :         if (count(L1_etObs(iDomain)%maskObs(iCell, :)) .LE. &
    2129             :                             (0.10_dp * real(size(L1_etObs(iDomain)%dataObs(:, :), dim = 2), dp))) then
    2130           0 :           invalid_cells = invalid_cells + 1.0_dp
    2131           0 :           cycle
    2132             :         end if
    2133             : 
    2134             :         ! calculate ojective function
    2135             :         objective_et_domain = objective_et_domain + &
    2136           0 :                 kge(L1_etObs(iDomain)%dataObs(iCell, :), etOptiSim(iDomain)%dataSim(iCell, :), &
    2137           0 :                                                    mask = L1_etObs(iDomain)%maskObs(iCell, :))
    2138             :       end do
    2139             : 
    2140             :       ! user information about invalid cells
    2141           0 :       if (invalid_cells .GT. 0.5_dp) then
    2142           0 :         call message('   WARNING: objective_et: Detected invalid cells in study area (.LT. 10 valid data points).')
    2143             :         call message('                          Fraction of invalid cells: ', &
    2144           0 :                 num2str(invalid_cells / real(nCells1, dp), '(F4.2)'))
    2145             :       end if
    2146             : 
    2147             :       ! calculate average soil moisture objective over all domains with power law
    2148             :       ! domains are weighted equally ( 1 / real(domainMeta%overallNumberOfDomains,dp))**6
    2149             :       objective_et = objective_et + &
    2150           0 :               ((1.0_dp - objective_et_domain / real(nCells1, dp)) / real(domainMeta%overallNumberOfDomains, dp))**6
    2151           0 :       call etOptiSim(iDomain)%destroy()
    2152             :     end do
    2153           0 :     deallocate(etOptiSim)
    2154             : 
    2155             :     ! compromise solution - sixth root
    2156           0 :     objective_et = objective_et**onesixth
    2157             : 
    2158             :     ! -----------------------------
    2159             :     ! RUNOFF
    2160             :     ! -----------------------------
    2161           0 :     objective_q = 0.0_dp
    2162           0 :     nGaugesTotal = size(runoff, dim = 2)
    2163             : 
    2164           0 :     do gg = 1, nGaugesTotal
    2165             : 
    2166             :       ! extract runoff
    2167           0 :       call extract_runoff(gg, runoff, runoff_agg, runoff_obs, runoff_obs_mask)
    2168             : 
    2169             :       ! KGE
    2170             :       objective_q = objective_q + &
    2171           0 :               ((1.0_dp - kge(runoff_obs, runoff_agg, mask = runoff_obs_mask)) / real(nGaugesTotal, dp))**6
    2172             : 
    2173             :     end do
    2174             : 
    2175           0 :     deallocate(runoff_agg, runoff_obs, runoff_obs_mask)
    2176             : 
    2177             :     ! compromise solution - sixth root
    2178           0 :     objective_q = objective_q**onesixth
    2179             : 
    2180             :     ! equal weighted compromise objective functions for discharge and soilmoisture
    2181             :     ! ToDo: why do we use the sixth root of of objective_sm and objective_kge
    2182             :     ! only to take the power to 6 here again, when we never need the
    2183             :     ! intermediate results?
    2184             : #ifdef MPI
    2185             :     objective_kge_q_et = (objective_et**6 + objective_q**6)
    2186             : #else
    2187           0 :     objective_kge_q_et = (objective_et**6 + objective_q**6)**onesixth
    2188             : 
    2189           0 :     call message('    objective_kge_q_et = ', num2str(objective_kge_q_et, '(F9.5)'))
    2190             : #endif
    2191             :     !    print*, "1-SM 2-Q : ", 1.0_dp-objective_sm, 1.0_dp-objective_kge ! MZMZMZMZ
    2192             : 
    2193           0 :   END FUNCTION objective_kge_q_et
    2194             : 
    2195             : 
    2196             :   ! -----------------------------------------------------------------
    2197             : 
    2198             :   !    NAME
    2199             :   !        objective_kge_q_BFI
    2200             : 
    2201             :   !    PURPOSE
    2202             :   !>       \brief Objective function of KGE for runoff and BFI absulute difference
    2203             : 
    2204             :   !>       \details Objective function of KGE for runoff and KGE for ET.
    2205             :   !>       Further details can be found in the documentation of objective functions
    2206             :   !>       '14 - objective_multiple_gauges_kge_power6'.
    2207             : 
    2208             :   !    INTENT(IN)
    2209             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    2210             :   !>       \param[in] "procedure(eval_interface) :: eval"
    2211             : 
    2212             :   !    RETURN
    2213             :   !>       \return real(dp) :: objective_kge_q_BFI — objective function value
    2214             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    2215             : 
    2216             :   !    HISTORY
    2217             :   !>       \authors Sebastian Müller
    2218             : 
    2219             :   !>       \date Apr 2022
    2220             : 
    2221           0 :   FUNCTION objective_kge_q_BFI(parameterset, eval)
    2222             : 
    2223           0 :     use mo_optimization_types, only : optidata_sim
    2224             :     use mo_common_variables, only : level1, domainMeta
    2225             :     use mo_errormeasures, only : kge
    2226             :     use mo_global_variables, only : BFI_obs
    2227             :     use mo_string_utils, only : num2str
    2228             :     use mo_mrm_objective_function_runoff, only : extract_runoff
    2229             : 
    2230             :     implicit none
    2231             : 
    2232             :     real(dp), dimension(:), intent(in) :: parameterset
    2233             : 
    2234             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    2235             : 
    2236             :     real(dp) :: objective_kge_q_BFI
    2237           0 :     real(dp) :: objective_BFI
    2238           0 :     real(dp) :: objective_q
    2239             : 
    2240             :     real(dp), parameter :: onesixth = 1.0_dp / 6.0_dp
    2241             : 
    2242             :     ! number of invalid cells in catchment
    2243             :     real(dp) :: invalid_cells
    2244             : 
    2245             :     ! modelled runoff for a given parameter set
    2246             :     ! dim1=nTimeSteps, dim2=nGauges
    2247           0 :     real(dp), allocatable, dimension(:, :) :: runoff
    2248             : 
    2249             :     ! domain loop counter
    2250             :     integer(i4) :: iDomain
    2251             : 
    2252             :     !> baseflow index for each domain
    2253           0 :     real(dp), dimension(:), allocatable :: BFI
    2254             : 
    2255             :     ! counter
    2256             :     integer(i4) :: gg, i
    2257             : 
    2258             :     integer(i4) :: nGaugesTotal
    2259             : 
    2260             :     ! aggregated simulated runoff
    2261           0 :     integer(i4), dimension(:), allocatable :: domain_ids, domain_ids_pack
    2262             : 
    2263             :     ! aggregated simulated runoff
    2264           0 :     real(dp), dimension(:), allocatable :: runoff_agg
    2265             : 
    2266             :     ! measured runoff
    2267           0 :     real(dp), dimension(:), allocatable :: runoff_obs
    2268             : 
    2269             :     ! mask for measured runoff
    2270           0 :     logical, dimension(:), allocatable :: runoff_obs_mask
    2271             : 
    2272             :     ! run mHM
    2273           0 :     allocate(BFI(domainMeta%nDomains))
    2274           0 :     call eval(parameterset, runoff = runoff, BFI = BFI)
    2275             : 
    2276             :     ! -----------------------------
    2277             :     ! BFI
    2278             :     ! -----------------------------
    2279             : 
    2280             :     ! initialize some variables
    2281           0 :     objective_BFI = 0.0_dp
    2282             : 
    2283           0 :     if ( any(BFI_obs < 0.0_dp) ) then
    2284           0 :       allocate(domain_ids(domainMeta%nDomains))
    2285           0 :       allocate(domain_ids_pack(count(BFI_obs < 0.0_dp)))
    2286           0 :       domain_ids = [(i, i=1,size(domain_ids))]
    2287           0 :       domain_ids_pack = pack(domain_ids, mask=(BFI_obs < 0.0_dp))
    2288             :       call error_message( &
    2289             :         "objective_kge_q_BFI: missing BFI values for domain ", &
    2290           0 :         trim(adjustl(num2str(domain_ids_pack(1)))) &
    2291           0 :       )
    2292             :     end if
    2293             : 
    2294             :     ! loop over domain - for applying power law later on
    2295           0 :     do iDomain = 1, domainMeta%nDomains
    2296           0 :       objective_BFI = objective_BFI + abs(BFI(iDomain) - BFI_obs(iDomain)) / domainMeta%nDomains
    2297             :     end do
    2298             : 
    2299             :     ! -----------------------------
    2300             :     ! RUNOFF
    2301             :     ! -----------------------------
    2302           0 :     objective_q = 0.0_dp
    2303           0 :     nGaugesTotal = size(runoff, dim = 2)
    2304             : 
    2305           0 :     do gg = 1, nGaugesTotal
    2306             : 
    2307             :       ! extract runoff
    2308           0 :       call extract_runoff(gg, runoff, runoff_agg, runoff_obs, runoff_obs_mask)
    2309             : 
    2310             :       ! KGE
    2311             :       objective_q = objective_q + &
    2312           0 :               ((1.0_dp - kge(runoff_obs, runoff_agg, mask = runoff_obs_mask)) / real(nGaugesTotal, dp))**6
    2313             : 
    2314             :     end do
    2315             : 
    2316           0 :     deallocate(runoff_agg, runoff_obs, runoff_obs_mask)
    2317             : 
    2318             :     ! compromise solution - sixth root
    2319           0 :     objective_q = objective_q**onesixth
    2320             : 
    2321           0 :     objective_kge_q_BFI = (objective_BFI + 1._dp)*objective_q
    2322           0 :     call message('    objective_kge_q_BFI = ', num2str(objective_kge_q_BFI, '(F9.5)'))
    2323             : 
    2324           0 :   END FUNCTION objective_kge_q_BFI
    2325             : 
    2326             : 
    2327             :   ! -----------------------------------------------------------------
    2328             : 
    2329             :   !    NAME
    2330             :   !        objective_kge_q_rmse_et
    2331             : 
    2332             :   !    PURPOSE
    2333             :   !>       \brief Objective function of KGE for runoff and RMSE for domain_avg ET (standarized scores)
    2334             : 
    2335             :   !>       \details Objective function of KGE for runoff and RMSE for domain_avg ET (standarized scores)
    2336             : 
    2337             :   !    INTENT(IN)
    2338             :   !>       \param[in] "real(dp), dimension(:) :: parameterset"
    2339             :   !>       \param[in] "procedure(eval_interface) :: eval"
    2340             : 
    2341             :   !    RETURN
    2342             :   !>       \return real(dp) :: objective_kge_q_rmse_et &mdash; objective function value
    2343             :   !>       (which will be e.g. minimized by an optimization routine like DDS)
    2344             : 
    2345             :   !    HISTORY
    2346             :   !>       \authors Johannes Brenner
    2347             : 
    2348             :   !>       \date July 2017
    2349             : 
    2350             :   ! Modifications:
    2351             :   ! Robert Schweppe Jun 2018 - refactoring and reformatting
    2352             : 
    2353           0 :   FUNCTION objective_kge_q_rmse_et(parameterset, eval)
    2354             : 
    2355           0 :     use mo_optimization_types, only : optidata_sim
    2356             :     use mo_common_constants, only : eps_dp, nodata_dp
    2357             :     use mo_common_mhm_mrm_variables, only : evalPer
    2358             :     use mo_common_variables, only : domainMeta
    2359             :     use mo_errormeasures, only : rmse
    2360             :     use mo_global_variables, only : L1_etObs
    2361             :     use mo_julian, only : caldat
    2362             :     use mo_moment, only : average, mean
    2363             :     use mo_standard_score, only : classified_standard_score
    2364             :     use mo_string_utils, only : num2str
    2365             :     use mo_temporal_aggregation, only : day2mon_average
    2366             :     use mo_errormeasures, only : kge
    2367             :     use mo_mrm_objective_function_runoff, only : extract_runoff
    2368             : 
    2369             :     implicit none
    2370             : 
    2371             :     real(dp), dimension(:), intent(in) :: parameterset
    2372             : 
    2373             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    2374             : 
    2375             :     real(dp) :: objective_kge_q_rmse_et
    2376             : 
    2377             :     ! modelled runoff for a given parameter set
    2378             :     ! dim1=nTimeSteps, dim2=nGauges
    2379           0 :     real(dp), allocatable, dimension(:, :) :: runoff
    2380             : 
    2381             :     !> simulated et
    2382           0 :     type(optidata_sim), dimension(:), allocatable :: etOptiSim
    2383             : 
    2384             :     ! time loop counter
    2385             :     integer(i4) :: iTime
    2386             : 
    2387             :     ! domain counter, month counters
    2388             :     integer(i4) :: iDomain, pp, mmm
    2389             : 
    2390             :     integer(i4) :: year, month, day
    2391             : 
    2392           0 :     real(dp), dimension(domainMeta%nDomains) :: initTime
    2393             : 
    2394             :     ! total number of months
    2395             :     integer(i4) :: nMonths
    2396             : 
    2397             :     ! vector with months' classes
    2398           0 :     integer(i4), dimension(:), allocatable :: month_classes
    2399             : 
    2400             :     ! monthly values original time series
    2401           0 :     real(dp), dimension(:), allocatable :: et_sim_m, et_obs_m
    2402             : 
    2403             :     ! monthly values anomaly time series
    2404           0 :     real(dp), dimension(:), allocatable :: et_sim_m_anom, et_obs_m_anom
    2405             : 
    2406           0 :     logical, dimension(:), allocatable :: et_obs_m_mask
    2407             : 
    2408             :     ! kge_q(nGaugesTotal)
    2409           0 :     real(dp), dimension(:), allocatable :: rmse_et
    2410             : 
    2411             :     ! obj. functions
    2412           0 :     real(dp) :: rmse_et_avg, kge_q_avg
    2413             : 
    2414             :     ! spatial average of observed et
    2415           0 :     real(dp), dimension(:), allocatable :: et_catch_avg_domain
    2416             : 
    2417             :     ! spatial avergae of modeled  et
    2418           0 :     real(dp), dimension(:), allocatable :: et_opti_catch_avg_domain
    2419             : 
    2420             :     ! mask for valid et catchment avg time steps
    2421           0 :     logical, dimension(:), allocatable :: mask_times
    2422             : 
    2423             :     ! rmse_et(domainMeta%nDomains)
    2424           0 :     real(dp), dimension(:), allocatable :: kge_q
    2425             : 
    2426             :     ! gauges counter
    2427             :     integer(i4) :: gg
    2428             : 
    2429             :     integer(i4) :: nGaugesTotal
    2430             : 
    2431             :     ! aggregated simulated runoff
    2432           0 :     real(dp), dimension(:), allocatable :: runoff_agg
    2433             : 
    2434             :     ! measured runoff
    2435           0 :     real(dp), dimension(:), allocatable :: runoff_obs
    2436             : 
    2437             :     ! mask for measured runoff
    2438           0 :     logical, dimension(:), allocatable :: runoff_obs_mask
    2439             : 
    2440             :     ! obtain simulation values of runoff (hourly) and ET
    2441             :     ! for ET only valid cells (domains concatenated)
    2442             :     ! et_opti: aggregate ET to needed time step for optimization (see timeStep_et_input)
    2443           0 :     allocate(etOptiSim(domainMeta%nDomains))
    2444           0 :     call eval(parameterset, runoff = runoff, etOptiSim = etOptiSim)
    2445             : 
    2446             :     !--------------------------------------------
    2447             :     !! EVAPOTRANSPIRATION
    2448             :     !--------------------------------------------
    2449             : 
    2450             :     ! allocate
    2451           0 :     allocate(rmse_et(domainMeta%nDomains))
    2452           0 :     rmse_et(:) = nodata_dp
    2453             : 
    2454           0 :     do iDomain = 1, domainMeta%nDomains
    2455             : 
    2456             :       ! allocate
    2457           0 :       allocate(mask_times              (size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2458           0 :       allocate(et_catch_avg_domain     (size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2459           0 :       allocate(et_opti_catch_avg_domain(size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2460             : 
    2461             :       ! initalize
    2462           0 :       mask_times = .TRUE.
    2463           0 :       et_catch_avg_domain = nodata_dp
    2464           0 :       et_opti_catch_avg_domain = nodata_dp
    2465             : 
    2466             :       ! calculate catchment average evapotranspiration
    2467           0 :       do iTime = 1, size(etOptiSim(iDomain)%dataSim, dim = 2)
    2468             :         ! check for enough data points in time for correlation
    2469           0 :         if (all(.NOT. L1_etObs(iDomain)%maskObs(:, iTime))) then
    2470             :           !write (*,*) 'WARNING: et data at time ', iTime, ' is empty.'
    2471             :           !call message('WARNING: objective_et_kge_catchment_avg: ignored current time step since less than')
    2472             :           !call message('         10 valid cells available in evapotranspiration observation')
    2473           0 :           mask_times(iTime) = .FALSE.
    2474           0 :           cycle
    2475             :         end if
    2476             :         ! spatial average of observed ET
    2477           0 :         et_catch_avg_domain(iTime) = average(L1_etObs(iDomain)%dataObs(:, iTime), &
    2478           0 :                                       mask = L1_etObs(iDomain)%maskObs(:, iTime))
    2479             :         ! spatial avergae of modeled ET
    2480           0 :         et_opti_catch_avg_domain(iTime) = average(etOptiSim(iDomain)%dataSim(:, iTime), &
    2481           0 :                                             mask = L1_etObs(iDomain)%maskObs(:, iTime))
    2482             :       end do
    2483             : 
    2484             :       ! get initial time of the evaluation period
    2485           0 :       initTime(iDomain) = real(evalPer(iDomain)%julStart, dp)
    2486             : 
    2487             :       ! get calendar days, months, year
    2488           0 :       call caldat(int(initTime(iDomain)), yy = year, mm = month, dd = day)
    2489             : 
    2490             :       ! if evapotranspiration input daily
    2491           0 :       select case(L1_etObs(iDomain)%timeStepInput)
    2492             :         ! JBJBJB
    2493             :         ! daily: aggregate to monthly mean
    2494             :       case(-1)
    2495             :         ! calculate monthly averages from daily values of the model
    2496           0 :         call day2mon_average(et_opti_catch_avg_domain, year, month, day, et_sim_m, misval = nodata_dp)
    2497             :         ! calculate monthly averages from daily values of the observations
    2498           0 :         call day2mon_average(et_catch_avg_domain, year, month, day, et_obs_m, misval = nodata_dp)
    2499             :         !
    2500             :         ! monthly: proceed without action
    2501             :       case(-2)
    2502             :         ! simulation
    2503           0 :         allocate(et_sim_m(size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2504           0 :         et_sim_m = et_opti_catch_avg_domain
    2505             :         ! observation
    2506           0 :         allocate(et_obs_m(size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2507           0 :         et_obs_m = et_catch_avg_domain
    2508             : 
    2509             :         ! yearly: ERROR stop program
    2510             :       case(-3)
    2511           0 :         call error_message('***ERROR: objective_kge_q_rmse_et: time step of evapotranspiration yearly.')
    2512             :       end select
    2513             :       ! remove mean from modelled time series
    2514           0 :       et_sim_m(:) = et_sim_m(:) - mean(et_sim_m(:))
    2515             :       ! remove mean from observed time series
    2516           0 :       et_obs_m(:) = et_obs_m(:) - mean(et_obs_m(:))
    2517             :       ! get number of months for given domain
    2518           0 :       nMonths = size(et_obs_m)
    2519           0 :       allocate (month_classes(nMonths))
    2520           0 :       allocate (et_obs_m_mask(nMonths))
    2521           0 :       allocate (et_obs_m_anom(nMonths))
    2522           0 :       allocate (et_sim_m_anom(nMonths))
    2523             : 
    2524           0 :       month_classes(:) = 0
    2525           0 :       et_obs_m_mask(:) = .TRUE.
    2526           0 :       et_obs_m_anom(:) = nodata_dp
    2527           0 :       et_sim_m_anom(:) = nodata_dp
    2528             : 
    2529             :       ! define months' classes
    2530           0 :       mmm = month
    2531           0 :       do pp = 1, nMonths
    2532           0 :         month_classes(pp) = mmm
    2533           0 :         if (mmm .LT. 12) then
    2534           0 :           mmm = mmm + 1
    2535             :         else
    2536             :           mmm = 1
    2537             :         end if
    2538             :       end do
    2539             :       ! double check missing data
    2540             :       ! define mask for missing data in observations (there are always data for simulations)
    2541           0 :       where(abs(et_obs_m - nodata_dp) .lt. eps_dp) et_obs_m_mask = .FALSE.
    2542             : 
    2543             :       ! calculate standard score
    2544           0 :       et_obs_m_anom = classified_standard_score(et_obs_m, month_classes, mask = et_obs_m_mask)
    2545           0 :       et_sim_m_anom = classified_standard_score(et_sim_m, month_classes, mask = et_obs_m_mask)
    2546           0 :       rmse_et(iDomain) = rmse(et_sim_m_anom, et_obs_m_anom, mask = et_obs_m_mask)
    2547             : 
    2548           0 :       deallocate (month_classes)
    2549           0 :       deallocate (et_obs_m)
    2550           0 :       deallocate (et_sim_m)
    2551           0 :       deallocate (et_obs_m_mask)
    2552           0 :       deallocate (et_sim_m_anom)
    2553           0 :       deallocate (et_obs_m_anom)
    2554             :       !end if
    2555             : 
    2556           0 :       call etOptiSim(iDomain)%destroy()
    2557             :     end do
    2558             : 
    2559             :     rmse_et_avg = sum(rmse_et(:), abs(rmse_et - nodata_dp) .gt. eps_dp) / &
    2560           0 :             real(count(abs(rmse_et - nodata_dp) .gt. eps_dp), dp)
    2561           0 :     deallocate(rmse_et)
    2562           0 :     deallocate(etOptiSim)
    2563             : 
    2564             :     !--------------------------------------------
    2565             :     !! RUNOFF
    2566             :     !--------------------------------------------
    2567           0 :     kge_q_avg = 0_dp
    2568           0 :     nGaugesTotal = size(runoff, dim = 2)
    2569           0 :     allocate(kge_q(nGaugesTotal))
    2570           0 :     kge_q(:) = nodata_dp
    2571             : 
    2572           0 :     do gg = 1, nGaugesTotal
    2573             : 
    2574             :       ! extract runoff
    2575           0 :       call extract_runoff(gg, runoff, runoff_agg, runoff_obs, runoff_obs_mask)
    2576             : 
    2577             :       ! check for whether to proceed with this domain or not
    2578             :       ! potentially 3 years of data
    2579             :       !pp = count(runoff_agg .ge. 0.0_dp )
    2580             :       !if (pp .lt.  365*3 ) then
    2581             :       !    deallocate (runoff_agg, runoff_obs, runoff_obs_mask)
    2582             :       !    cycle
    2583             :       ! else
    2584             :       ! calculate KGE for each domain:
    2585           0 :       kge_q(gg) = kge(runoff_obs, runoff_agg, mask = runoff_obs_mask)
    2586           0 :       deallocate (runoff_agg, runoff_obs, runoff_obs_mask)
    2587             :       ! end if
    2588             : 
    2589             :     end do
    2590             : 
    2591             :     ! calculate average KGE value for runoff
    2592             :     kge_q_avg = sum(kge_q(:), abs(kge_q - nodata_dp) .gt. eps_dp) / &
    2593           0 :             real(count(abs(kge_q - nodata_dp) .gt. eps_dp), dp)
    2594           0 :     deallocate(kge_q)
    2595             : 
    2596             :     !
    2597           0 :     objective_kge_q_rmse_et = rmse_et_avg * (1._dp - kge_q_avg)
    2598             : 
    2599           0 :     call message('    objective_kge_q_rmse_et = ', num2str(objective_kge_q_rmse_et, '(F9.5)'))
    2600             : 
    2601           0 :   END FUNCTION objective_kge_q_rmse_et
    2602             : 
    2603          12 :   subroutine create_domain_avg_tws(iDomain, twsOptiSim, tws_catch_avg_domain, &
    2604             :                                            tws_opti_catch_avg_domain, mask_times)
    2605           0 :     use mo_optimization_types, only : optidata_sim
    2606             :     use mo_common_constants, only : nodata_dp
    2607             :     use mo_global_variables, only : L1_twsaObs
    2608             :     use mo_moment, only : average
    2609             :     ! current domain Id
    2610             :     integer(i4), intent(in) :: iDomain
    2611             : 
    2612             :     ! simulated tws
    2613             :     type(optidata_sim), dimension(:), intent(in) :: twsOptiSim
    2614             : 
    2615             :     ! aggregated simulated
    2616             :     real(dp), dimension(:), allocatable, intent(out) :: tws_catch_avg_domain
    2617             : 
    2618             :     ! extracted measured
    2619             :     real(dp), dimension(:), allocatable, intent(out) :: tws_opti_catch_avg_domain
    2620             : 
    2621             :     ! mask of no data values
    2622             :     logical, dimension(:), allocatable, intent(out) :: mask_times
    2623             : 
    2624             :     ! local
    2625             :     ! time loop counter
    2626             :     integer(i4) :: iTime
    2627             : 
    2628             :     ! allocate
    2629          18 :     allocate(mask_times               (size(twsOptiSim(iDomain)%dataSim, dim = 2)))
    2630          18 :     allocate(tws_catch_avg_domain     (size(twsOptiSim(iDomain)%dataSim, dim = 2)))
    2631          18 :     allocate(tws_opti_catch_avg_domain(size(twsOptiSim(iDomain)%dataSim, dim = 2)))
    2632             : 
    2633             :     ! initalize
    2634         156 :     mask_times = .TRUE.
    2635         156 :     tws_catch_avg_domain = nodata_dp
    2636         156 :     tws_opti_catch_avg_domain = nodata_dp
    2637             : 
    2638             :     ! calculate catchment average evapotranspiration
    2639         150 :     do iTime = 1, size(twsOptiSim(iDomain)%dataSim, dim = 2)
    2640             : 
    2641             :       ! check for enough data points in time for correlation
    2642         144 :       if (all(.NOT. L1_twsaObs(iDomain)%maskObs(:, iTime))) then
    2643             :         !write (*,*) 'WARNING: et data at time ', iTime, ' is empty.'
    2644             :         !call message('WARNING: objective_et_kge_catchment_avg: ignored current time step since less than')
    2645             :         !call message('         10 valid cells available in evapotranspiration observation')
    2646           0 :         mask_times(iTime) = .FALSE.
    2647           0 :         cycle
    2648             :       end if
    2649             : 
    2650           0 :       tws_catch_avg_domain(iTime) = average(L1_twsaObs(iDomain)%dataObs(:, iTime), &
    2651         144 :                                      mask = L1_twsaObs(iDomain)%maskObs(:, iTime))
    2652         144 :       tws_opti_catch_avg_domain(iTime) = average(twsOptiSim(iDomain)%dataSim(:, iTime), &
    2653         294 :                                      mask = L1_twsaObs(iDomain)%maskObs(:, iTime))
    2654             :     end do
    2655             : 
    2656           6 :   end subroutine create_domain_avg_tws
    2657             : 
    2658           0 :   subroutine create_domain_avg_et(iDomain, etOptiSim, et_catch_avg_domain, &
    2659             :                                            et_opti_catch_avg_domain, mask_times)
    2660           6 :     use mo_optimization_types, only : optidata_sim
    2661             :     use mo_common_constants, only : nodata_dp
    2662             :     use mo_global_variables, only : L1_etObs
    2663             :     use mo_moment, only : average
    2664             :     ! current domain Id
    2665             :     integer(i4), intent(in) :: iDomain
    2666             : 
    2667             :     ! simulated et
    2668             :     type(optidata_sim), dimension(:), intent(in) :: etOptiSim
    2669             : 
    2670             :     ! aggregated simulated
    2671             :     real(dp), dimension(:), allocatable, intent(out) :: et_catch_avg_domain
    2672             : 
    2673             :     ! extracted measured
    2674             :     real(dp), dimension(:), allocatable, intent(out) :: et_opti_catch_avg_domain
    2675             : 
    2676             :     ! mask of no data values
    2677             :     logical, dimension(:), allocatable, intent(out) :: mask_times
    2678             : 
    2679             :     ! local
    2680             :     ! time loop counter
    2681             :     integer(i4) :: iTime
    2682             : 
    2683             :     ! allocate
    2684           0 :     allocate(mask_times              (size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2685           0 :     allocate(et_catch_avg_domain     (size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2686           0 :     allocate(et_opti_catch_avg_domain(size(etOptiSim(iDomain)%dataSim, dim = 2)))
    2687             : 
    2688             :     ! initalize
    2689           0 :     mask_times = .TRUE.
    2690           0 :     et_catch_avg_domain = nodata_dp
    2691           0 :     et_opti_catch_avg_domain = nodata_dp
    2692             : 
    2693             :     ! calculate catchment average evapotranspiration
    2694           0 :     do iTime = 1, size(etOptiSim(iDomain)%dataSim, dim = 2)
    2695             : 
    2696             :       ! check for enough data points in time for correlation
    2697           0 :       if (all(.NOT. L1_etObs(iDomain)%maskObs(:, iTime))) then
    2698             :         !write (*,*) 'WARNING: et data at time ', iTime, ' is empty.'
    2699             :         !call message('WARNING: objective_et_kge_catchment_avg: ignored current time step since less than')
    2700             :         !call message('         10 valid cells available in evapotranspiration observation')
    2701           0 :         mask_times(iTime) = .FALSE.
    2702           0 :         cycle
    2703             :       end if
    2704             : 
    2705           0 :       et_catch_avg_domain(iTime) = average(L1_etObs(iDomain)%dataObs(:, iTime), &
    2706           0 :                                     mask = L1_etObs(iDomain)%maskObs(:, iTime))
    2707           0 :       et_opti_catch_avg_domain(iTime) = average(etOptiSim(iDomain)%dataSim(:, iTime), &
    2708           0 :                                           mask = L1_etObs(iDomain)%maskObs(:, iTime))
    2709             :     end do
    2710             : 
    2711           0 :   end subroutine create_domain_avg_et
    2712             : 
    2713          21 :   subroutine convert_tws_to_twsa(twsOptiSim, L1_twsaObs, twsaOptiSim)
    2714           0 :     use mo_optimization_types, only : optidata_sim, optidata
    2715             :     use mo_moment, only : average
    2716             :     ! simulated tws
    2717             :     type(optidata_sim), intent(in)    :: twsOptiSim
    2718             :     ! observed twsa
    2719             :     type(optidata),     intent(in)    :: L1_twsaObs
    2720             :     ! simulated twsa
    2721             :     type(optidata_sim), intent(inout) :: twsaOptiSim
    2722             : 
    2723             :     ! local
    2724             :     integer(i4) :: iCell
    2725          21 :     real(dp)    :: twsa_av_cell
    2726             : 
    2727          84 :     allocate(twsaOptiSim%dataSim(size(twsOptiSim%dataSim(:, :), dim = 1), size(twsOptiSim%dataSim(:, :), dim = 2)))
    2728             : 
    2729         735 :     do iCell = 1, size(twsOptiSim%dataSim(:, :), dim = 1)
    2730         714 :       twsa_av_cell = average(twsOptiSim%dataSim(iCell, :), mask = L1_twsaObs%maskObs(iCell, :))
    2731       35007 :       twsaOptiSim%dataSim(iCell, :) = twsOptiSim%dataSim(iCell, :) - twsa_av_cell
    2732             :     end do
    2733             : 
    2734          21 :   end subroutine convert_tws_to_twsa
    2735             : 
    2736             : END MODULE mo_objective_function

Generated by: LCOV version 1.16