LCOV - code coverage report
Current view: top level - src - farming_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 77.4 % 133 103
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 2 2

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : MODULE farming_methods
      10              :    USE cp_files,                        ONLY: close_file,&
      11              :                                               open_file
      12              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      13              :                                               cp_logger_type
      14              :    USE cp_output_handling,              ONLY: cp_print_key_finished_output,&
      15              :                                               cp_print_key_generate_filename,&
      16              :                                               cp_print_key_unit_nr
      17              :    USE farming_types,                   ONLY: farming_env_type,&
      18              :                                               init_job_type,&
      19              :                                               job_finished,&
      20              :                                               job_pending,&
      21              :                                               job_running
      22              :    USE input_section_types,             ONLY: section_vals_get,&
      23              :                                               section_vals_get_subs_vals,&
      24              :                                               section_vals_type,&
      25              :                                               section_vals_val_get
      26              :    USE message_passing,                 ONLY: mp_para_env_type
      27              : #include "./base/base_uses.f90"
      28              : 
      29              :    IMPLICIT NONE
      30              :    PRIVATE
      31              :    PUBLIC  :: farming_parse_input, get_next_job
      32              : 
      33              :    ! must be negative in order to avoid confusion with job numbers
      34              :    INTEGER, PARAMETER, PUBLIC    :: do_nothing = -1, &
      35              :                                     do_wait = -2, &
      36              :                                     do_deadlock = -3
      37              : 
      38              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'farming_methods'
      39              : 
      40              : CONTAINS
      41              : 
      42              : ! **************************************************************************************************
      43              : !> \brief ...
      44              : !> \param farming_env ...
      45              : !> \param start ...
      46              : !> \param END ...
      47              : !> \param current ...
      48              : !> \param todo ...
      49              : ! **************************************************************************************************
      50           20 :    SUBROUTINE get_next_job(farming_env, start, END, current, todo)
      51              :       TYPE(farming_env_type), POINTER                    :: farming_env
      52              :       INTEGER, INTENT(IN)                                :: start, END
      53              :       INTEGER, INTENT(INOUT)                             :: current
      54              :       INTEGER, INTENT(OUT)                               :: todo
      55              : 
      56              :       INTEGER                                            :: icheck, idep, itry, ndep
      57              :       LOGICAL                                            :: dep_ok
      58              : 
      59           20 :       IF (farming_env%cycle) THEN
      60            0 :          IF (current < start) THEN
      61            0 :             current = start
      62              :          ELSE
      63            0 :             current = current + 1
      64              :          END IF
      65            0 :          IF (current > END) THEN
      66            0 :             todo = do_nothing
      67              :          ELSE
      68            0 :             todo = MODULO(current - 1, farming_env%njobs) + 1
      69              :          END IF
      70              :       ELSE
      71              :          ! find a pending job
      72           20 :          itry = start
      73           20 :          todo = do_nothing
      74          110 :          DO itry = start, END
      75          110 :             IF (farming_env%job(itry)%status == job_pending) THEN
      76              : 
      77              :                ! see if all dependencies are OK
      78           22 :                ndep = SIZE(farming_env%job(itry)%dependencies)
      79           22 :                dep_ok = .TRUE.
      80           30 :                dep: DO idep = 1, ndep
      81          128 :                   DO icheck = start, END
      82          110 :                      IF (farming_env%job(icheck)%status /= job_finished) THEN
      83           42 :                         IF (farming_env%job(icheck)%id == farming_env%job(itry)%dependencies(idep)) THEN
      84              :                            dep_ok = .FALSE.
      85              :                            EXIT dep
      86              :                         END IF
      87              :                      END IF
      88              :                   END DO
      89              :                END DO dep
      90              : 
      91              :                ! if there are pending jobs, the minion can not be told to stop
      92              :                ! at least wait if there are unresolved dependencies
      93           22 :                IF (dep_OK) THEN
      94           18 :                   todo = itry
      95           18 :                   EXIT
      96              :                ELSE
      97            4 :                   todo = do_wait
      98              :                END IF
      99              :             END IF
     100              :          END DO
     101              :          ! If we have to wait, but there are no running jobs we are deadlocked
     102              :          ! which we signal
     103           20 :          IF (todo == do_wait) THEN
     104              :             dep_OK = .FALSE.
     105            0 :             DO itry = start, END
     106            0 :                IF (farming_env%job(itry)%status == job_running) dep_OK = .TRUE.
     107              :             END DO
     108            0 :             IF (.NOT. dep_OK) todo = do_deadlock
     109              :          END IF
     110              :       END IF
     111           20 :    END SUBROUTINE get_next_job
     112              : 
     113              : ! **************************************************************************************************
     114              : !> \brief ...
     115              : !> \param farming_env ...
     116              : !> \param root_section ...
     117              : !> \param para_env ...
     118              : ! **************************************************************************************************
     119           24 :    SUBROUTINE farming_parse_input(farming_env, root_section, para_env)
     120              :       TYPE(farming_env_type), POINTER                    :: farming_env
     121              :       TYPE(section_vals_type), POINTER                   :: root_section
     122              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     123              : 
     124              :       CHARACTER(LEN=3)                                   :: text
     125              :       INTEGER                                            :: i, iunit, n_rep_val, num_minions, &
     126              :                                                             output_unit, stat
     127           24 :       INTEGER, DIMENSION(:), POINTER                     :: dependencies, i_vals
     128              :       LOGICAL                                            :: exists, explicit, has_dep
     129              :       TYPE(cp_logger_type), POINTER                      :: logger
     130              :       TYPE(section_vals_type), POINTER                   :: farming_section, jobs_section, print_key
     131              : 
     132           24 :       NULLIFY (farming_section, jobs_section, print_key, logger, dependencies, i_vals)
     133           48 :       logger => cp_get_default_logger()
     134           24 :       farming_env%group_size_wish_set = .FALSE.
     135           24 :       farming_env%ngroup_wish_set = .FALSE.
     136           24 :       farming_section => section_vals_get_subs_vals(root_section, "FARMING")
     137              : 
     138           24 :       IF (ASSOCIATED(farming_env%group_partition)) THEN
     139            0 :          DEALLOCATE (farming_env%group_partition)
     140              :       END IF
     141              : 
     142              :       ! The following input order is used
     143              :       ! 1) GROUP_PARTITION
     144              :       ! 2) NGROUP
     145              :       ! 3) GROUP_SIZE (default 8)
     146              :       CALL section_vals_val_get(farming_section, "GROUP_PARTITION", &
     147           24 :                                 n_rep_val=n_rep_val)
     148           24 :       IF (n_rep_val > 0) THEN
     149              :          CALL section_vals_val_get(farming_section, "GROUP_PARTITION", &
     150            0 :                                    i_vals=i_vals)
     151            0 :          ALLOCATE (farming_env%group_partition(0:SIZE(i_vals) - 1))
     152            0 :          farming_env%group_partition(:) = i_vals
     153            0 :          farming_env%ngroup_wish_set = .TRUE.
     154            0 :          farming_env%ngroup_wish = SIZE(i_vals)
     155              :       ELSE
     156              :          CALL section_vals_val_get(farming_section, "NGROUP", &
     157           24 :                                    n_rep_val=n_rep_val)
     158           24 :          IF (n_rep_val > 0) THEN
     159              :             CALL section_vals_val_get(farming_section, "NGROUP", &
     160           20 :                                       i_val=farming_env%ngroup_wish)
     161           20 :             farming_env%ngroup_wish_set = .TRUE.
     162              :          ELSE
     163              :             CALL section_vals_val_get(farming_section, "GROUP_SIZE", &
     164            4 :                                       i_val=farming_env%group_size_wish)
     165            4 :             farming_env%group_size_wish_set = .TRUE.
     166              :          END IF
     167              :       END IF
     168              :       CALL section_vals_val_get(farming_section, "STRIDE", &
     169           24 :                                 i_val=farming_env%stride)
     170              : 
     171              :       CALL section_vals_val_get(farming_section, "RESTART_FILE_NAME", &
     172           24 :                                 explicit=explicit)
     173           24 :       IF (explicit) THEN
     174              :          CALL section_vals_val_get(farming_section, "RESTART_FILE_NAME", &
     175            2 :                                    c_val=farming_env%restart_file_name)
     176              :       ELSE
     177           22 :          print_key => section_vals_get_subs_vals(farming_section, "RESTART")
     178              :          farming_env%restart_file_name = cp_print_key_generate_filename(logger, print_key, extension=".restart", &
     179           22 :                                                                         my_local=.FALSE.)
     180              :       END IF
     181              : 
     182              :       CALL section_vals_val_get(farming_section, "DO_RESTART", &
     183           24 :                                 l_val=farming_env%restart)
     184              :       CALL section_vals_val_get(farming_section, "MAX_JOBS_PER_GROUP", &
     185           24 :                                 i_val=farming_env%max_steps)
     186              :       CALL section_vals_val_get(farming_section, "CYCLE", &
     187           24 :                                 l_val=farming_env%cycle)
     188              :       CALL section_vals_val_get(farming_section, "WAIT_TIME", &
     189           24 :                                 r_val=farming_env%wait_time)
     190              : 
     191              :       CALL section_vals_val_get(farming_section, "CAPTAIN_MINION", &
     192           24 :                                 l_val=farming_env%captain_minion)
     193              : 
     194           24 :       jobs_section => section_vals_get_subs_vals(farming_section, "JOB")
     195           24 :       CALL section_vals_get(jobs_section, n_repetition=farming_env%njobs)
     196              : 
     197          232 :       ALLOCATE (farming_env%Job(farming_env%njobs))
     198          184 :       CALL init_job_type(farming_env%job)
     199              : 
     200           24 :       has_dep = .FALSE.
     201          184 :       DO i = 1, farming_env%njobs
     202              :          CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     203          160 :                                    keyword_name="DIRECTORY", c_val=farming_env%Job(i)%cwd)
     204              :          CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     205          160 :                                    keyword_name="INPUT_FILE_NAME", c_val=farming_env%Job(i)%input)
     206              :          CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     207          160 :                                    keyword_name="OUTPUT_FILE_NAME", c_val=farming_env%Job(i)%output)
     208              : 
     209              :          ! if job id is not specified the job id is the index
     210              :          CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     211          160 :                                    keyword_name="JOB_ID", n_rep_val=n_rep_val)
     212          160 :          IF (n_rep_val == 0) THEN
     213          142 :             farming_env%Job(i)%id = i
     214              :          ELSE
     215              :             CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     216           18 :                                       keyword_name="JOB_ID", i_val=farming_env%Job(i)%id)
     217              :          END IF
     218              : 
     219              :          ! get dependencies
     220              :          CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     221          160 :                                    keyword_name="DEPENDENCIES", n_rep_val=n_rep_val)
     222          344 :          IF (n_rep_val == 0) THEN
     223          146 :             ALLOCATE (farming_env%Job(i)%dependencies(0))
     224              :          ELSE
     225              :             CALL section_vals_val_get(jobs_section, i_rep_section=i, &
     226           14 :                                       keyword_name="DEPENDENCIES", i_vals=dependencies)
     227           42 :             ALLOCATE (farming_env%Job(i)%dependencies(SIZE(dependencies, 1)))
     228           46 :             farming_env%Job(i)%dependencies = dependencies
     229           14 :             IF (SIZE(dependencies, 1) /= 0) has_dep = .TRUE.
     230              :          END IF
     231              :       END DO
     232              : 
     233           24 :       IF (has_dep) THEN
     234            2 :          CPASSERT(farming_env%captain_minion)
     235            2 :          CPASSERT(.NOT. farming_env%cycle)
     236              :       END IF
     237              : 
     238              :       output_unit = cp_print_key_unit_nr(logger, farming_section, "PROGRAM_RUN_INFO", &
     239           24 :                                          extension=".log")
     240              : 
     241              :       ! Captain/Minion not supported
     242           24 :       IF (para_env%num_pe == 1) THEN
     243            0 :          farming_env%captain_minion = .FALSE.
     244            0 :          WRITE (output_unit, FMT="(T2,A)") "FARMING| Captain-Minion setup not supported for serial runs"
     245              :       END IF
     246           24 :       IF (farming_env%captain_minion) THEN
     247            4 :          num_minions = para_env%num_pe - 1
     248              :       ELSE
     249           20 :          num_minions = para_env%num_pe
     250              :       END IF
     251              : 
     252           24 :       IF (output_unit > 0) THEN
     253           12 :          WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Number of jobs found", farming_env%njobs
     254           12 :          IF (farming_env%ngroup_wish_set) THEN
     255           10 :             WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Ngroup wish:", farming_env%ngroup_wish
     256           10 :             IF (ASSOCIATED(farming_env%group_partition)) THEN
     257            0 :                WRITE (output_unit, FMT="(T2,A)", ADVANCE="NO") "FARMING| User partition:"
     258            0 :                DO i = 0, SIZE(farming_env%group_partition) - 1
     259            0 :                   IF (MODULO(i, 4) == 0) WRITE (output_unit, *)
     260            0 :                   WRITE (output_unit, FMT='(I4)', ADVANCE="NO") farming_env%group_partition(i)
     261              :                END DO
     262            0 :                WRITE (output_unit, *)
     263            0 :                IF (SUM(farming_env%group_partition) /= num_minions) THEN
     264              :                   WRITE (output_unit, FMT="(T2,A,T61,I10,T71,I10)") &
     265            0 :                      "FARMING| WARNING : group partition CPUs not equal to the available number (ignoring Captain) ", &
     266            0 :                      num_minions, SUM(farming_env%group_partition)
     267            0 :                   WRITE (output_unit, FMT="(T2,A)") "FARMING|          partition data ignored" ! any better idea ??
     268            0 :                   DEALLOCATE (farming_env%group_partition)
     269              :                END IF
     270              :             END IF
     271              :          END IF
     272           12 :          IF (farming_env%group_size_wish_set) THEN
     273            2 :             WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Group size wish:", &
     274            4 :                farming_env%group_size_wish
     275              :          END IF
     276           12 :          WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Max steps      :", farming_env%max_steps
     277           12 :          IF (farming_env%cycle) THEN
     278            1 :             text = "YES"
     279              :          ELSE
     280           11 :             text = " NO"
     281              :          END IF
     282           12 :          WRITE (output_unit, FMT="(T2,A,T78,A3)") "FARMING| Cyclic jobs execution:", text
     283           12 :          IF (farming_env%restart) THEN
     284            1 :             text = "YES"
     285              :          ELSE
     286           11 :             text = " NO"
     287              :          END IF
     288           12 :          WRITE (output_unit, FMT="(T2,A,T78,A3)") "FARMING| Restarting farm:", text
     289           12 :          farming_env%restart_n = 1
     290           12 :          IF (farming_env%restart) THEN
     291              :             ! A missing restart file is not an error: the farm then starts with the first job
     292            1 :             INQUIRE (FILE=farming_env%restart_file_name, EXIST=exists)
     293            1 :             IF (exists) THEN
     294              :                CALL open_file(unit_number=iunit, &
     295            1 :                               file_name=farming_env%restart_file_name)
     296            1 :                READ (UNIT=iunit, FMT=*, IOSTAT=stat) farming_env%restart_n
     297            1 :                IF (stat /= 0) THEN
     298            0 :                   farming_env%restart_n = 1
     299              :                   WRITE (UNIT=output_unit, FMT="(T2,A)") &
     300              :                      "FARMING| ---- WARNING ---- failed to read from ("// &
     301            0 :                      TRIM(farming_env%restart_file_name)//") starting at 1"
     302              :                ELSE
     303              :                   WRITE (UNIT=output_unit, FMT="(T2,A)") &
     304            1 :                      "FARMING| restarting from ("//TRIM(farming_env%restart_file_name)//")"
     305              :                   WRITE (UNIT=output_unit, FMT="(T2,A,T71,I10)") &
     306            1 :                      "FARMING| restarting at ", farming_env%restart_n
     307              :                END IF
     308            1 :                CALL close_file(unit_number=iunit)
     309              :             ELSE
     310              :                WRITE (UNIT=output_unit, FMT="(T2,A)") &
     311              :                   "FARMING| ---- WARNING ---- restart file ("// &
     312            0 :                   TRIM(farming_env%restart_file_name)//") not found, starting at 1"
     313              :             END IF
     314              :          END IF
     315              : 
     316              :          CALL cp_print_key_finished_output(output_unit, logger, farming_section, &
     317           12 :                                            "PROGRAM_RUN_INFO")
     318              :       END IF
     319           24 :       CALL para_env%bcast(farming_env%restart_n)
     320              : 
     321           72 :    END SUBROUTINE farming_parse_input
     322              : 
     323              : END MODULE farming_methods
        

Generated by: LCOV version 2.0-1