LCOV - code coverage report
Current view: top level - mHM - mo_mhm_interface.F90 (source / functions) Hit Total Coverage
Test: mHM coverage Lines: 97 108 89.8 %
Date: 2025-10-15 15:00:58 Functions: 4 6 66.7 %

          Line data    Source code
       1             : !> \file    mo_mhm_interface.f90
       2             : !> \brief   \copybrief mo_mhm_interface
       3             : !> \details \copydetails mo_mhm_interface
       4             : 
       5             : !> \brief   Module providing interfaces for mHM.
       6             : !> \details Interfaces to control the mHM workflow from outside (init, run, get infos, etc.).
       7             : !> \authors Sebastian Mueller
       8             : !> \version 0.1
       9             : !> \date    Oct 2021
      10             : !> \copyright Copyright 2005-\today, the mHM Developers, Luis Samaniego, Sabine Attinger: All rights reserved.
      11             : !! mHM is released under the LGPLv3+ license \license_note
      12             : !> \ingroup f_mhm
      13             : module mo_mhm_interface
      14             : 
      15             :   use mo_kind, only: i4, dp
      16             :   use mo_message, only: message, error_message
      17             :   use mo_string_utils, only: num2str
      18             : 
      19             : #ifdef MPI
      20             :   use mpi_f08
      21             : #endif
      22             : 
      23             :   implicit none
      24             : 
      25             :   private
      26             : 
      27             :   public :: mhm_interface_init
      28             :   public :: mhm_interface_get_parameter
      29             :   public :: mhm_interface_get_parameter_number
      30             :   public :: mhm_interface_run
      31             :   public :: mhm_interface_run_optimization
      32             :   public :: mhm_interface_finalize
      33             : 
      34             : contains
      35             : 
      36             :   !> \brief initialize mHM from given namelist paths.
      37          28 :   subroutine mhm_interface_init(namelist_mhm, namelist_mhm_param, namelist_mhm_output, namelist_mrm_output, cwd)
      38             :     use mo_file, only: &
      39             :       file_namelist_mhm, &
      40             :       file_namelist_mhm_param, &
      41             :       file_defOutput
      42             :     use mo_mrm_file, only: mrm_file_defOutput => file_defOutput
      43             :     use mo_common_read_config, only: &
      44             :       common_read_config
      45             :     use mo_common_mHM_mRM_read_config, only : &
      46             :       common_mHM_mRM_read_config, &
      47             :       check_optimization_settings
      48             :     use mo_mpr_read_config, only: mpr_read_config
      49             :     use mo_mhm_read_config, only: mhm_read_config
      50             :     use mo_read_wrapper, only : read_data
      51             :     use mo_mrm_init, only: &
      52             :       mrm_init, &
      53             :       mrm_configuration
      54             :     use mo_common_variables, only: &
      55             :       level0, &
      56             :       level1, &
      57             :       itimer, &
      58             :       domainMeta, &
      59             :       processMatrix
      60             :     use mo_common_mHM_mRM_variables, only : &
      61             :       timeStep, &
      62             :       simPer, &
      63             :       optimize, &
      64             :       opti_function, &
      65             :       mrm_coupling_mode, &
      66             :       read_restart
      67             :     use mo_mhm_messages, only: &
      68             :       startup_message, &
      69             :       domain_dir_check_message
      70             :     use mo_timer, only: &
      71             :       timers_init, &
      72             :       timer_start, &
      73             :       timer_stop, &
      74             :       timer_get
      75             :     use mo_startup, only: mhm_initialize
      76             :     use mo_global_variables, only: &
      77             :       couple_cfg, &
      78             :       meteo_handler, &
      79             :       L1_twsaObs, &
      80             :       L1_etObs, &
      81             :       L1_neutronsObs, &
      82             :       L1_smObs, &
      83             :       BFI_calc
      84             :     use mo_read_optional_data, only: readOptidataObs
      85             :     use mo_write_ascii, only: write_configfile
      86             :     use mo_mhm_bfi, only: calculate_BFI
      87             :     use mo_os, only: change_dir
      88             : 
      89             :     implicit none
      90             : 
      91             :     character(*), optional, intent(in) :: namelist_mhm !< path to mHM configuration namelist
      92             :     character(*), optional, intent(in) :: namelist_mhm_param !< path to mHM parameter namelist
      93             :     character(*), optional, intent(in) :: namelist_mhm_output !< path to mHM output namelist
      94             :     character(*), optional, intent(in) :: namelist_mrm_output !< path to mRM output namelist
      95             :     character(*), optional, intent(in) :: cwd !< desired working directory
      96             : 
      97             :     integer(i4) :: domainID, iDomain
      98             : 
      99             : #ifdef MPI
     100             :     integer             :: ierror
     101             :     integer(i4)         :: nproc, rank
     102             : #endif
     103             : 
     104             :     ! reset nml paths if wanted
     105           0 :     if (present(namelist_mhm)) file_namelist_mhm = namelist_mhm
     106          14 :     if (present(namelist_mhm_param)) file_namelist_mhm_param = namelist_mhm_param
     107          14 :     if (present(namelist_mhm_output)) file_defOutput = namelist_mhm_output
     108          14 :     if (present(namelist_mrm_output)) mrm_file_defOutput = namelist_mrm_output
     109             :     ! change working directory
     110          14 :     if (present(cwd)) call change_dir(cwd)
     111             : 
     112             :     ! startup message
     113          14 :     call startup_message()
     114             : 
     115             :     ! coupling configuration
     116          14 :     call couple_cfg%read_config(file_namelist_mhm)
     117             :     ! read configs
     118          14 :     call common_read_config(file_namelist_mhm)
     119          14 :     call mpr_read_config(file_namelist_mhm, file_namelist_mhm_param)
     120          14 :     call common_mHM_mRM_read_config(file_namelist_mhm)
     121          14 :     call mhm_read_config(file_namelist_mhm)
     122          14 :     call couple_cfg%check(domainMeta, optimize)
     123          14 :     call meteo_handler%config(file_namelist_mhm, optimize, domainMeta, processMatrix, timestep, couple_cfg)
     124          14 :     mrm_coupling_mode = 2_i4 ! TODO: this shouldn't be needed
     125          14 :     call mrm_configuration(file_namelist_mhm, file_namelist_mhm_param)
     126          14 :     if (optimize) call check_optimization_settings()
     127             : 
     128             :     ! Message about input directories
     129          14 :     call domain_dir_check_message()
     130             : 
     131             :     ! Start timings
     132          14 :     call timers_init
     133             : 
     134             :     ! --------------------------------------------------------------------------
     135             :     ! READ AND INITIALIZE
     136             :     ! --------------------------------------------------------------------------
     137          14 :     itimer = 1
     138             : #ifdef MPI
     139             :     call MPI_Comm_size(domainMeta%comMaster, nproc, ierror)
     140             :     ! find the number the process is referred to, called rank
     141             :     call MPI_Comm_rank(domainMeta%comMaster, rank, ierror)
     142             :     ! ComLocal is a communicator, i.e. a group of processes assigned to the same
     143             :     ! domain, with a master and subprocesses. Only the master processes of these
     144             :     ! groups need to read the data. The master process with rank 0 only
     145             :     ! coordinates the other processes and does not need to read the data.
     146             :     if (rank > 0 .and. domainMeta%isMasterInComLocal) then
     147             : #endif
     148          14 :     call message()
     149             : 
     150          14 :     if (.not. read_restart) then
     151          13 :       call message('  Read data ...')
     152          13 :       call timer_start(itimer)
     153             :       ! for DEM, slope, ... define nGvar local
     154             :       ! read_data has a domain loop inside
     155          13 :       call read_data(simPer)
     156          13 :       call timer_stop(itimer)
     157          13 :       call message('    in ', trim(num2str(timer_get(itimer), '(F9.3)')), ' seconds.')
     158             :     end if
     159             : 
     160             :     ! read data for every domain
     161          14 :     itimer = itimer + 1
     162          14 :     call message('  Initialize domains ...')
     163          14 :     call timer_start(itimer)
     164          14 :     call mhm_initialize()
     165          14 :     call meteo_handler%init_level2(level0, level1)
     166          14 :     call timer_stop(itimer)
     167          14 :     call message('  in ', trim(num2str(timer_get(itimer), '(F9.3)')), ' seconds.')
     168          27 :     if (processMatrix(8, 1) > 0) &
     169          13 :         call mrm_init(file_namelist_mhm, file_namelist_mhm_param)
     170             : 
     171          14 :     itimer = itimer + 1
     172          14 :     call message('  Read forcing and optional data ...')
     173          14 :     call timer_start(itimer)
     174             : 
     175          40 :     do iDomain = 1, domainMeta%nDomains
     176          26 :       domainID = domainMeta%indices(iDomain)
     177             :       ! read meteorology now, if it should be loaded in one go
     178          26 :       if (meteo_handler%single_read(iDomain)) call meteo_handler%prepare_data(1, iDomain, level1, simPer)
     179             : 
     180             :       ! read optional optional data if necessary
     181          40 :       if (optimize) then
     182           1 :         select case (opti_function)
     183             :           case(10 : 13, 28)
     184             :             ! read optional spatio-temporal soil mositure data
     185           1 :             call readOptidataObs(iDomain, domainID, L1_smObs(iDomain))
     186             :           case(17)
     187             :             ! read optional spatio-temporal neutrons data
     188           1 :             call readOptidataObs(iDomain, domainID, L1_neutronsObs(iDomain))
     189             :           case(27, 29, 30)
     190             :             ! read optional spatio-temporal evapotranspiration data
     191           0 :             call readOptidataObs(iDomain, domainID, L1_etObs(iDomain))
     192             :           case(15)
     193             :             ! read optional spatio-temporal tws data
     194           1 :             call readOptidataObs(iDomain, domainID, L1_twsaObs(iDomain))
     195             :           case(33)
     196             :             ! read optional spatio-temporal evapotranspiration data
     197           6 :             if (domainMeta%optidata(iDomain) == 0 .or. domainMeta%optidata(iDomain) == 5 .or. &
     198             :               domainMeta%optidata(iDomain) == 6 ) then
     199           3 :               call readOptidataObs(iDomain, domainID, L1_etObs(iDomain))
     200             :             end if
     201             :             ! read optional spatio-temporal tws data
     202           6 :             if (domainMeta%optidata(iDomain) == 0 .or. domainMeta%optidata(iDomain) == 3 .or. &
     203          13 :               domainMeta%optidata(iDomain) == 6 ) then
     204           3 :               call readOptidataObs(iDomain, domainID, L1_twsaObs(iDomain))
     205             :             end if
     206             :         end select
     207             :       end if
     208             :     end do
     209             : 
     210             :     ! calculate observed BFI if wanted
     211          14 :     if ( optimize .and. opti_function==34 .and. BFI_calc ) call calculate_BFI()
     212             : 
     213          14 :     call timer_stop(itimer)
     214          14 :     call message('    in ', trim(num2str(timer_get(itimer), '(F9.3)')), ' seconds.')
     215             : 
     216             :     !this call may be moved to another position as it writes the master config out file for all domains
     217             :     call write_configfile(meteo_handler%dirPrecipitation, meteo_handler%dirReferenceET, meteo_handler%dirTemperature)
     218             : 
     219             : #ifdef MPI
     220             :     end if
     221             : #endif
     222             : 
     223          28 :   end subroutine mhm_interface_init
     224             : 
     225             :   !> \brief Get current global parameter value of mHM.
     226           0 :   subroutine mhm_interface_get_parameter(para)
     227          14 :     use mo_common_variables, only: global_parameters
     228             : 
     229             :     implicit none
     230             : 
     231             :     real(dp), dimension(:), allocatable, intent(out) :: para !< global parameter values of mHM
     232             : 
     233           0 :     allocate(para(size(global_parameters, dim=1)))
     234             : 
     235           0 :     para = global_parameters(:, 3)
     236             : 
     237           0 :   end subroutine mhm_interface_get_parameter
     238             : 
     239             :   !> \brief Get number of current global parameter value of mHM.
     240           0 :   subroutine mhm_interface_get_parameter_number(n)
     241           0 :     use mo_common_variables, only: global_parameters
     242             : 
     243             :     implicit none
     244             : 
     245             :     integer(i4), intent(out) :: n !< number of global parameter values of mHM
     246             : 
     247           0 :     n = size(global_parameters, dim=1)
     248             : 
     249           0 :   end subroutine mhm_interface_get_parameter_number
     250             : 
     251             :   !> \brief Run mHM with current settings.
     252           9 :   subroutine mhm_interface_run()
     253             : #ifdef MPI
     254             :     use mo_common_variables, only: domainMeta
     255             : #endif
     256             :     use mo_common_variables, only: &
     257           0 :       itimer, &
     258             :       global_parameters
     259             :     use mo_timer, only: &
     260             :       timer_start, &
     261             :       timer_stop, &
     262             :       timer_get
     263             :     use mo_mhm_eval, only: mhm_eval
     264             : 
     265             :     implicit none
     266             : 
     267             : #ifdef MPI
     268             :     integer             :: ierror
     269             :     integer(i4)         :: nproc, rank
     270             : 
     271             :     call MPI_Comm_size(domainMeta%comMaster, nproc, ierror)
     272             :     ! find the number the process is referred to, called rank
     273             :     call MPI_Comm_rank(domainMeta%comMaster, rank, ierror)
     274             : #endif
     275             : 
     276           9 :     itimer = itimer + 1
     277             : 
     278             :     ! --------------------------------------------------------------------------
     279             :     ! call mHM
     280             :     ! get runoff timeseries if possible (i.e. when domainMeta%doRouting,
     281             :     ! processMatrix(8,1) > 0)
     282             :     ! get other model outputs  (i.e. gridded fields of model output)
     283             :     ! --------------------------------------------------------------------------
     284             : 
     285             : #ifdef MPI
     286             :     if (rank > 0 .and. domainMeta%isMasterInComLocal) then
     287             : #endif
     288             : 
     289           9 :     call message('  Run mHM')
     290           9 :     call timer_start(itimer)
     291           9 :     call mhm_eval(global_parameters(:, 3))
     292           9 :     call timer_stop(itimer)
     293           9 :     call message('    in ', trim(num2str(timer_get(itimer), '(F12.3)')), ' seconds.')
     294             : 
     295             : #ifdef MPI
     296             :     endif
     297             : #endif
     298             : 
     299           9 :   end subroutine mhm_interface_run
     300             : 
     301             :   !> \brief Run mHM optimization with current settings.
     302           5 :   subroutine mhm_interface_run_optimization()
     303             : #ifdef MPI
     304             :     use mo_common_variables, only: domainMeta
     305             :     use mo_objective_function, only: &
     306             :       objective_subprocess, &
     307             :       objective_master
     308             :     use mo_mrm_objective_function_runoff, only: &
     309             :       single_objective_runoff_master, &
     310             :       single_objective_runoff_subprocess
     311             : #endif
     312             :     use mo_common_variables, only: &
     313             :       itimer, &
     314             :       dirConfigOut, &
     315             :       global_parameters,&
     316           9 :       global_parameters_name, &
     317             :       processMatrix
     318             :     use mo_common_mHM_mRM_variables, only : &
     319             :       opti_function
     320             :     use mo_timer, only: &
     321             :       timer_start, &
     322             :       timer_stop, &
     323             :       timer_get
     324             :     use mo_mhm_eval, only: mhm_eval
     325             :     use mo_objective_function, only: objective
     326             :     use mo_optimization, only: optimization
     327             :     use mo_mrm_objective_function_runoff, only: single_objective_runoff
     328             :     use mo_write_ascii, only: &
     329             :       write_optifile, &      ! Writing optimized parameter set and objective
     330             :       write_optinamelist     ! Writing optimized parameter set to a namelist
     331             : 
     332             :     implicit none
     333             : 
     334             :     procedure(mhm_eval), pointer :: eval
     335             :     procedure(objective), pointer :: obj_func
     336             : 
     337           5 :     real(dp) :: funcbest         ! best objective function achivied during optimization
     338           5 :     logical, dimension(:), allocatable :: maskpara ! true  = parameter will be optimized, = parameter(i,4) = 1
     339             :     !                                              ! false = parameter will not be optimized = parameter(i,4) = 0
     340             : 
     341             : #ifdef MPI
     342             :     integer             :: ierror
     343             :     integer(i4)         :: nproc, rank
     344             : 
     345             :     call MPI_Comm_size(domainMeta%comMaster, nproc, ierror)
     346             :     ! find the number the process is referred to, called rank
     347             :     call MPI_Comm_rank(domainMeta%comMaster, rank, ierror)
     348             : #endif
     349             : 
     350           5 :     itimer = itimer + 1
     351           5 :     call message('  Run mHM optimization')
     352           5 :     call timer_start(itimer)
     353             : 
     354           5 :     eval => mhm_eval
     355             : 
     356           1 :     select case(opti_function)
     357             :       case(1 : 9, 14, 31 : 32)
     358             :         ! call optimization against only runoff (no other variables)
     359           1 :         obj_func => single_objective_runoff
     360             : #ifdef MPI
     361             :         if (rank == 0 .and. domainMeta%isMasterInComLocal) then
     362             :           obj_func => single_objective_runoff_master
     363             :           call optimization(eval, obj_func, dirConfigOut, funcBest, maskpara)
     364             :         else if (domainMeta%isMasterInComLocal) then
     365             :           ! In case of a master process from ComLocal, i.e. a master of a group of
     366             :           ! processes that are assigned to a single domain, this process calls the
     367             :           ! objective subroutine directly. The master over all processes collects
     368             :           ! the data and runs the dds/sce/other opti method.
     369             :           call single_objective_runoff_subprocess(eval)
     370             :         end if
     371             : #else
     372           1 :         call optimization(eval, obj_func, dirConfigOut, funcBest, maskpara)
     373             : #endif
     374             : 
     375             :       case(10 : 13, 15, 17, 27, 28, 29, 30, 33, 34)
     376             :         ! call optimization for other variables
     377           4 :         obj_func => objective
     378             : #ifdef MPI
     379             :         if (rank == 0 .and. domainMeta%isMasterInComLocal) then
     380             :           obj_func => objective_master
     381             :           call optimization(eval, obj_func, dirConfigOut, funcBest, maskpara)
     382             :         else if (domainMeta%isMasterInComLocal) then
     383             :           ! In case of a master process from ComLocal, i.e. a master of a group of
     384             :           ! processes that are assigned to a single domain, this process calls the
     385             :           ! objective subroutine directly. The master over all processes collects
     386             :           ! the data and runs the dds/sce/other opti method.
     387             :           call objective_subprocess(eval)
     388             :         end if
     389             : #else
     390           4 :         call optimization(eval, obj_func, dirConfigOut, funcBest, maskpara)
     391             : #endif
     392             : 
     393             :       case default
     394             :         call error_message('***ERROR: mhm_driver: The given objective function number ', &
     395          10 :                            trim(adjustl(num2str(opti_function))), ' in mhm.nml is not valid!')
     396             :     end select
     397             : 
     398             : #ifdef MPI
     399             :     if (rank == 0 .and. domainMeta%isMasterInComLocal) then
     400             : #endif
     401             : 
     402             :     ! write a file with final objective function and the best parameter set
     403           5 :     call write_optifile(funcbest, global_parameters(:, 3), global_parameters_name(:))
     404             :     ! write a file with final best parameter set in a namlist format
     405           5 :     call write_optinamelist(processMatrix, global_parameters, maskpara, global_parameters_name(:))
     406           5 :     deallocate(maskpara)
     407             : 
     408             : #ifdef MPI
     409             :     end if
     410             : #endif
     411             : 
     412           5 :     call timer_stop(itimer)
     413           5 :     call message('    in ', trim(num2str(timer_get(itimer), '(F12.3)')), ' seconds.')
     414             : 
     415           5 :   end subroutine mhm_interface_run_optimization
     416             : 
     417             :   !> \brief Write mHM restart.
     418          14 :   subroutine mhm_interface_finalize()
     419             : #ifdef MPI
     420             :     use mo_common_variables, only: domainMeta
     421             : #endif
     422             :     use mo_common_variables, only: &
     423             :       itimer, &
     424             :       mhmFileRestartOut, &
     425           5 :       write_restart, &
     426             :       processMatrix
     427             :     use mo_common_mHM_mRM_variables, only : &
     428             :       optimize
     429             :     use mo_timer, only: &
     430             :       timer_start, &
     431             :       timer_stop, &
     432             :       timer_get
     433             :     use mo_restart, only: write_restart_files
     434             :     use mo_mrm_write, only : mrm_write
     435             :     use mo_mhm_messages, only: finish_message
     436             :     use mo_clean_up, only: deallocate_global_variables
     437             : 
     438             :     implicit none
     439             : 
     440             : #ifdef MPI
     441             :     integer             :: ierror
     442             :     integer(i4)         :: nproc, rank
     443             : 
     444             :     call MPI_Comm_size(domainMeta%comMaster, nproc, ierror)
     445             :     ! find the number the process is referred to, called rank
     446             :     call MPI_Comm_rank(domainMeta%comMaster, rank, ierror)
     447             :     if (rank > 0 .and. domainMeta%isMasterInComLocal) then
     448             : #endif
     449             : 
     450             :     ! --------------------------------------------------------------------------
     451             :     ! WRITE RESTART files
     452             :     ! --------------------------------------------------------------------------
     453          23 :     if (write_restart  .AND. (.NOT. optimize)) then
     454           9 :       itimer = itimer + 1
     455           9 :       call message()
     456           9 :       call message('  Write restart file')
     457           9 :       call timer_start(itimer)
     458             :       call write_restart_files(mhmFileRestartOut)
     459           9 :       call timer_stop(itimer)
     460           9 :       call message('    in ', trim(num2str(timer_get(itimer), '(F9.3)')), ' seconds.')
     461             :     end if
     462             : 
     463             :     ! --------------------------------------------------------------------------
     464             :     ! WRITE RUNOFF (INCLUDING RESTART FILES, has to be called after mHM restart
     465             :     ! files are written)
     466             :     ! --------------------------------------------------------------------------
     467          14 :     if (processMatrix(8, 1) > 0) call mrm_write()
     468             : 
     469             : #ifdef MPI
     470             :     end if
     471             : #endif
     472             : 
     473          14 :     call finish_message()
     474             : 
     475             :     ! clean up all allocated variables
     476          14 :     call deallocate_global_variables()
     477             : 
     478          14 :   end subroutine mhm_interface_finalize
     479             : 
     480             : end module mo_mhm_interface

Generated by: LCOV version 1.16