add a master-slave mode for doing farming.

This improves load balance in the case where jobs of unequal lenght are being processed.
Allow for dependencies between jobs in a farming run.


svn-origin-rev: 6390
This commit is contained in:
Joost VandeVondele 2007-09-28 13:44:15 +00:00
parent 6cf31a5ed3
commit c1cfd7176f
19 changed files with 1139 additions and 155 deletions

View file

@ -36,10 +36,15 @@ MODULE cp2k_runs
f_env_add_defaults,&
f_env_rm_defaults,&
f_env_type
USE farming_methods, ONLY: farming_parse_input
USE farming_methods, ONLY: farming_parse_input, &
get_next_job, &
do_nothing, &
do_wait, &
do_deadlock
USE farming_types, ONLY: deallocate_farming_env,&
farming_env_type,&
init_farming_env
init_farming_env,&
job_pending,job_running,job_finished
USE fist_debug, ONLY: fist_debug_control => debug_control
USE force_env_methods, ONLY: force_env_calc_energy_force
USE force_env_types, ONLY: force_env_get,&
@ -71,21 +76,20 @@ MODULE cp2k_runs
dp
USE library_tests, ONLY: lib_test
USE machine, ONLY: m_chdir,&
m_flush,&
m_getcwd,&
m_walltime
USE mc_run, ONLY: do_mon_car
USE md_run, ONLY: qs_mol_dyn
USE message_passing, ONLY: mp_comm_free,&
mp_comm_split,&
mp_environ,&
mp_sum,&
mp_sync
USE message_passing, ONLY: &
mp_any_source, mp_bcast, mp_comm_dup, mp_comm_free, mp_comm_split, &
mp_environ, mp_recv, mp_send, mp_sum, mp_sync
USE neb_methods, ONLY: neb
USE pint_methods, ONLY: do_pint_run
USE qs_debug, ONLY: qs_debug_energy_and_forces
USE qs_linres_module, ONLY: linres_calculation
USE qs_tddfpt_module, ONLY: tddfpt_calculation
USE reference_manager, ONLY: print_all_references, &
USE reference_manager, ONLY: print_all_references,&
print_format_html
USE timings, ONLY: timeset,&
timestop
@ -427,15 +431,22 @@ CPSourceFileRef,&
CHARACTER(len=*), PARAMETER :: routineN = 'farming_run', &
routineP = moduleN//':'//routineN
INTEGER, PARAMETER :: slave_status_done = -3, &
slave_status_wait = -4
CHARACTER(len=5) :: label
CHARACTER(LEN=default_path_length) :: output_file
CHARACTER(LEN=default_string_length) :: str
INTEGER :: handle, i, i_job_to_restart, ierr, ijob, ijob_end, ijob_start, &
iunit, n_jobs_to_run, new_group, new_output_unit, new_rank, new_size, &
ngroups, output_unit, stat
INTEGER, DIMENSION(:), POINTER :: group_distribution
LOGICAL :: failure, found, run_OK
INTEGER :: dest, handle, i, i_job_to_restart, ierr, ijob, ijob_end, &
ijob_start, ijob_current, iunit, n_jobs_to_run, new_group, new_output_unit, new_rank, &
new_size, ngroups, num_slaves, output_unit, primus_slave, slave_group, &
slave_rank, source, stat, tag, todo
INTEGER, DIMENSION(:), POINTER :: group_distribution, &
master_slave_partition, &
slave_distribution, &
slave_status
LOGICAL :: failure, found, master, &
run_OK, slave
REAL(KIND=dp) :: t1, t2
REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: waittime
TYPE(cp_logger_type), POINTER :: logger
@ -445,6 +456,8 @@ CPSourceFileRef,&
TYPE(section_type), POINTER :: g_section
TYPE(section_vals_type), POINTER :: g_data
! the primus of all slaves, talks to the master on topics concerning all slaves
failure=.FALSE.
CALL timeset(routineN,'I','',handle)
NULLIFY(my_parser,g_section,g_data,default_units)
@ -462,25 +475,94 @@ CPSourceFileRef,&
CALL m_getcwd(farming_env%cwd)
CALL farming_parse_input(farming_env,root_section,para_env,error)
! split the current communicator
ALLOCATE(group_distribution(0:para_env%num_pe-1),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
IF (farming_env%group_size_wish_set) THEN
CALL mp_comm_split(para_env%group,new_group,ngroups,group_distribution,&
subgroup_min_size=farming_env%group_size_wish)
ELSE IF (farming_env%ngroup_wish_set) THEN
CALL mp_comm_split(para_env%group,new_group,ngroups,group_distribution,&
n_subgroups=farming_env%ngroup_wish,&
group_partition=farming_env%group_partition)
ELSE
CPPostcondition(.FALSE.,cp_failure_level,routineP,error,failure)
ENDIF
CALL mp_environ(new_size,new_rank,new_group)
! the full mpi group is first split in a slave group and a master group, the latter being at most 1 process
slave=.TRUE.
master=.FALSE.
IF (farming_env%master_slave) THEN
IF (output_unit>0) WRITE(output_unit,FMT="(T2,A)") "FARMING| using a master-slave setup"
ALLOCATE(master_slave_partition(0:1))
master_slave_partition=(/1,para_env%num_pe-1/)
ALLOCATE(group_distribution(0:para_env%num_pe-1),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
CALL mp_comm_split(para_env%group,slave_group,ngroups,group_distribution,&
n_subgroups=2, group_partition=master_slave_partition)
DEALLOCATE(master_slave_partition)
DEALLOCATE(group_distribution)
CALL mp_environ(num_slaves,slave_rank,slave_group)
IF (para_env%mepos==0) THEN
slave =.FALSE.
master=.TRUE.
! on the master node, num_slaves corresponds to the size of the master group
! due to the mp_environ call.
CPPostcondition(num_slaves==1,cp_failure_level,routineP,error,failure)
num_slaves=para_env%num_pe-1
slave_rank=-1
ENDIF
CPPostcondition(num_slaves==para_env%num_pe-1,cp_failure_level,routineP,error,failure)
ELSE
! all processes are slaves
IF (output_unit>0) WRITE(output_unit,FMT="(T2,A)") "FARMING| using a slave-only setup"
CALL mp_comm_dup(para_env%group,slave_group)
CALL mp_environ(num_slaves,slave_rank,slave_group)
ENDIF
IF (output_unit>0) WRITE(output_unit,FMT="(T2,A,I0)") "FARMING| number of slaves ",num_slaves
! keep track of which para_env rank is which slave/master
ALLOCATE(slave_distribution(0:para_env%num_pe-1),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
slave_distribution=0
slave_distribution(para_env%mepos)=slave_rank
CALL mp_sum(slave_distribution,para_env%group)
! we do have a primus inter pares
primus_slave=0
DO i=1,para_env%num_pe-1
IF (slave_distribution(i)==0) primus_slave=i
ENDDO
! split the current communicator for the slaves
! in a new_group, new_size and new_rank according to the number of groups required according to the input
ALLOCATE(group_distribution(0:num_slaves-1),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
group_distribution=-1
IF (slave) THEN
IF (farming_env%group_size_wish_set) THEN
CALL mp_comm_split(slave_group,new_group,ngroups,group_distribution,&
subgroup_min_size=farming_env%group_size_wish)
ELSE IF (farming_env%ngroup_wish_set) THEN
CALL mp_comm_split(slave_group,new_group,ngroups,group_distribution,&
n_subgroups=farming_env%ngroup_wish,&
group_partition=farming_env%group_partition)
ELSE
CPPostcondition(.FALSE.,cp_failure_level,routineP,error,failure)
ENDIF
CALL mp_environ(new_size,new_rank,new_group)
ENDIF
! transfer the info about the slave group distribution to the master
IF (farming_env%master_slave) THEN
IF (para_env%mepos==primus_slave) THEN
tag=1
CALL mp_send(group_distribution,0,tag,para_env%group)
tag=2
CALL mp_send(ngroups,0,tag,para_env%group)
ENDIF
IF (para_env%mepos==0) THEN
tag=1
CALL mp_recv(group_distribution,primus_slave,tag,para_env%group)
tag=2
CALL mp_recv(ngroups,primus_slave,tag,para_env%group)
ENDIF
ENDIF
! write info on group distribution
IF (output_unit>0) THEN
WRITE(output_unit,FMT="(T2,A,T71,I10)") "FARMING| Number of created MPI groups:",ngroups
WRITE(output_unit,FMT="(T2,A)",ADVANCE="NO") "FARMING| Task to group correspondence:"
DO i=0,para_env%num_pe-1
WRITE(output_unit,FMT="(T2,A,T71,I10)") "FARMING| Number of created MPI (slave) groups:",ngroups
WRITE(output_unit,FMT="(T2,A)",ADVANCE="NO") "FARMING| MPI (slave) process to group correspondence:"
DO i=0,num_slaves-1
IF (MODULO(i,4)==0) WRITE(output_unit,*)
WRITE(output_unit,FMT='(A3,I4,A3,I4,A1)',ADVANCE="NO")&
" (",i," : ",group_distribution(i),")"
@ -489,10 +571,9 @@ CPSourceFileRef,&
ENDIF
! protect about too many jobs being run in single go. Not more jobs are allowed than the number in the input file
! and determine the restart point
! and determine the future restart point
IF (farming_env%cycle) THEN
n_jobs_to_run=MIN(farming_env%njobs,farming_env%max_steps*ngroups)
n_jobs_to_run=farming_env%max_steps*ngroups
i_job_to_restart=MODULO(farming_env%restart_n+n_jobs_to_run-1,farming_env%njobs)+1
ELSE
n_jobs_to_run=MIN(farming_env%njobs,farming_env%max_steps*ngroups)
@ -500,7 +581,7 @@ CPSourceFileRef,&
i_job_to_restart=n_jobs_to_run+farming_env%restart_n
ENDIF
! and write it now, that's the point where the next job starts, even if this one is running
! and write the restart now, that's the point where the next job starts, even if this one is running
IF (para_env%ionode) THEN
iunit=get_unit_number()
OPEN(UNIT=iunit,FILE="RESTARTFARM")
@ -508,112 +589,171 @@ CPSourceFileRef,&
CLOSE(UNIT=iunit)
ENDIF
! this is the job range to be executed. Will be treated cycle
! this is the job range to be executed.
ijob_start=farming_env%restart_n
ijob_end=ijob_start+n_jobs_to_run-1
IF (output_unit>0) THEN
IF (ijob_end-ijob_start<0) THEN
WRITE(output_unit,FMT="(T2,A)") "FARMING| --- WARNING --- NO JOBS NEED EXECUTION ? "
WRITE(output_unit,FMT="(T2,A)") "FARMING| is the cycle keyword required ?"
WRITE(output_unit,FMT="(T2,A)") "FARMING| or is a stray RESTARTFARM present ?"
ELSE
WRITE(output_unit,FMT="(T2,A)") "FARMING| List of jobs : "
DO ijob=ijob_start,ijob_end
i=MODULO(ijob-1,farming_env%njobs)+1
WRITE(output_unit,FMT=*) "Job: ",i," Dir: ",TRIM(farming_env%Job_cwd(i))," Input: ", &
TRIM(farming_env%Job_input(i))," MPI group:", MODULO(i-1,ngroups)
ENDDO
ENDIF
IF (output_unit>0 .AND. ijob_end-ijob_start<0) THEN
WRITE(output_unit,FMT="(T2,A)") "FARMING| --- WARNING --- NO JOBS NEED EXECUTION ? "
WRITE(output_unit,FMT="(T2,A)") "FARMING| is the cycle keyword required ?"
WRITE(output_unit,FMT="(T2,A)") "FARMING| or is a stray RESTARTFARM present ?"
WRITE(output_unit,FMT="(T2,A)") "FARMING| or is the group_size requested smaller than the number of CPUs?"
ENDIF
! actual executions of the jobs in two different modes
IF (farming_env%master_slave) THEN
IF (slave) THEN
! keep on doing work until master has decided otherwise
todo=do_wait
DO
IF (new_rank==0) THEN
! the head slave tells the master he's done or ready to start
! the message tells what has been done lately
tag=1
dest=0
CALL mp_send(todo,dest,tag,para_env%group)
DO ijob=ijob_start,ijob_end
i=MODULO(ijob-1,farming_env%njobs)+1
! this farms out the jobs
IF (MODULO(ijob-1,ngroups)==group_distribution(para_env%mepos)) THEN
! change to the new working directory
CALL m_chdir(TRIM(farming_env%Job_cwd(i)),ierr)
! gets the new todo item
tag=2
source=0
CALL mp_recv(todo,source,tag,para_env%group)
! generate a fresh call to cp2k_run
IF (ierr==0) THEN
IF (new_rank == 0) THEN
! and informs his peer slaves
CALL mp_bcast(todo,0,new_group)
ELSE
CALL mp_bcast(todo,0,new_group)
ENDIF
! if the todo is do_nothing we are flagged to quit. Otherwise it is the job number
SELECT CASE(todo)
CASE(do_wait,do_deadlock)
! go for a next round, but we first wait a bit
t1=m_walltime()
DO
t2=m_walltime()
IF (t2-t1>farming_env%wait_time) EXIT
ENDDO
CASE(do_nothing)
EXIT
CASE(1:)
CALL execute_job(todo)
END SELECT
ENDDO
ELSE ! master
ALLOCATE(slave_status(0:ngroups-1),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
slave_status=slave_status_wait
ijob_current=ijob_start-1
! generate the output file
WRITE(output_file,'(A12,I5.5)') "FARMING_OUT_",i
CALL parser_create(my_parser,file_name=TRIM(farming_env%Job_input(i)),error=error)
label="&GLOBAL"
CALL parser_search_string(my_parser,label,ignore_case=.TRUE.,found=found,error=error)
IF (found) THEN
CALL create_global_section(g_section,error=error)
CALL section_vals_create(g_data,g_section,error=error)
CALL cp_unit_set_create(default_units, "OUTPUT",error=error)
CALL section_vals_parse(g_data,my_parser,default_units,&
error=error)
CALL cp_unit_set_release(default_units,error=error)
CALL section_vals_val_get(g_data,"PROJECT",&
c_val=str, error=error)
IF (str.NE."") output_file=TRIM(str)//".out"
CALL section_vals_val_get(g_data,"OUTPUT_FILE_NAME",&
c_val=str,error=error)
IF (str.NE."") output_file=str
CALL section_vals_release(g_data,error=error)
CALL section_release(g_section,error=error)
END IF
CALL parser_release(my_parser,error=error)
CALL open_file(file_name=TRIM(output_file),&
file_action="WRITE",&
file_status="UNKNOWN",&
file_position="APPEND",&
unit_number=new_output_unit)
ELSE
! this should never be used by cp2k_run, but e.g. still happens, incorrectly,
! on eror, writes now to fort.999 so that error messages can be recovered and fixed
new_output_unit=999
ENDIF
DO
IF (ALL(slave_status==slave_status_done)) EXIT
IF (output_unit > 0) WRITE(output_unit,FMT="(T2,A,I5.5,A)") " Running Job ",i, &
" output to "//TRIM(farming_env%Job_cwd(i))//"/"//TRIM(output_file)
run_OK=cp2k_run(TRIM(farming_env%Job_input(i)),new_output_unit,new_group)
! who's the next slave waiting for work
tag=1
source=mp_any_source
CALL mp_recv(todo,source,tag,para_env%group) ! updates source
IF (todo>0) THEN
farming_env%Job(todo)%status=job_finished
IF (output_unit>0) THEN
WRITE(output_unit,FMT=*) "Job finished: ",todo
CALL m_flush(output_unit)
ENDIF
ENDIF
IF (new_rank == 0) CALL close_file(unit_number=new_output_unit)
! get the next job in line, this could be do_nothing, if we're finished
CALL get_next_job(farming_env,ijob_start,ijob_end,ijob_current,todo)
dest=source
tag =2
CALL mp_send(todo,dest,tag,para_env%group)
ELSE ! problem if just one node does not change...
IF (output_unit > 0 ) WRITE(output_unit,FMT="(T2,A,A)") &
" ... Failed to change dir to "//TRIM(farming_env%Job_cwd(i))
ENDIF
IF (todo>0) THEN
farming_env%Job(todo)%status=job_running
IF (output_unit>0) THEN
WRITE(output_unit,FMT=*) "Job: ",todo," Dir: ",TRIM(farming_env%Job(todo)%cwd), &
" assigned to group ",group_distribution(slave_distribution(dest))
CALL m_flush(output_unit)
ENDIF
ELSE
IF (todo==do_nothing) THEN
slave_status(group_distribution(slave_distribution(dest)))=slave_status_done
IF (output_unit>0) THEN
WRITE(output_unit,FMT=*) "group done: ",group_distribution(slave_distribution(dest))
CALL m_flush(output_unit)
ENDIF
ENDIF
IF (todo==do_deadlock) THEN
IF (output_unit>0) THEN
WRITE(output_unit,FMT=*) ""
WRITE(output_unit,FMT=*) "FARMING JOB DEADLOCKED ... CIRCULAR DEPENDENCIES"
WRITE(output_unit,FMT=*) ""
CALL m_flush(output_unit)
ENDIF
CPPostcondition(todo.NE.do_deadlock,cp_failure_level,routineP,error,failure)
ENDIF
ENDIF
! change to the original working directory
CALL m_chdir(TRIM(farming_env%cwd),ierr)
IF (ierr/=0) THEN
IF (output_unit > 0) WRITE(output_unit,FMT="(T2,A,A)") &
" ... Failed to change back home to "//TRIM(farming_env%cwd)
ENDIF
ENDDO
DEALLOCATE(slave_status,STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
ENDIF
ENDDO
ELSE
! this is the non-master-slave mode way of executing the jobs
! the i-th job in the input is always executed by the MODULO(i-1,ngroups)-th group
! (needed for cyclic runs, we don't want two groups working on the same job)
IF (output_unit>0) THEN
IF (ijob_end-ijob_start>=0) THEN
WRITE(output_unit,FMT="(T2,A)") "FARMING| List of jobs : "
DO ijob=ijob_start,ijob_end
i=MODULO(ijob-1,farming_env%njobs)+1
WRITE(output_unit,FMT=*) "Job: ",i," Dir: ",TRIM(farming_env%Job(i)%cwd)," Input: ", &
TRIM(farming_env%Job(i)%input)," MPI group:", MODULO(i-1,ngroups)
ENDDO
ENDIF
ENDIF
DO ijob=ijob_start,ijob_end
i=MODULO(ijob-1,farming_env%njobs)+1
! this farms out the jobs
IF (MODULO(i-1,ngroups)==group_distribution(slave_rank)) THEN
IF (output_unit > 0) WRITE(output_unit,FMT="(T2,A,I5.5,A)",ADVANCE="NO") " Running Job ",i, &
" in "//TRIM(farming_env%Job(i)%cwd)//"."
CALL execute_job(i)
IF (output_unit > 0) WRITE(output_unit,FMT="(A)") " Done, output in "//TRIM(output_file)
ENDIF
ENDDO
ENDIF
! keep information about how long each process has to wait
! i.e. the load imbalance
t1=m_walltime()
CALL mp_sync(para_env%group)
t2=m_walltime()
ALLOCATE(waittime(0:ngroups-1))
ALLOCATE(waittime(0:para_env%num_pe-1))
waittime=0.0_dp
IF (new_rank==0) waittime(group_distribution(para_env%mepos))=t2-t1
waittime(para_env%mepos)=t2-t1
CALL mp_sum(waittime,para_env%group)
IF (output_unit>0) THEN
WRITE(output_unit,'(T2,A)') "Group idle times [s] at the end of the run"
DO i=0,ngroups-1
WRITE(output_unit,'(T2,A)') "Process idle times [s] at the end of the run"
DO i=0,para_env%num_pe-1
WRITE(output_unit,FMT='(A2,I4,A3,F8.3,A1)',ADVANCE="NO")&
" (",i," : ",waittime(i),")"
IF (MOD(i+1,4)==0) WRITE(output_unit,'(A)') ""
ENDDO
CALL m_flush(output_unit)
ENDIF
DEALLOCATE(waittime)
! give back the communicators of the split groups
CALL mp_comm_free(new_group)
IF (slave) CALL mp_comm_free(new_group)
CALL mp_comm_free(slave_group)
! and message passing deallocate structures
DEALLOCATE(group_distribution,STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
DEALLOCATE(slave_distribution,STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
! clean the farming env
CALL deallocate_farming_env(farming_env)
@ -622,6 +762,59 @@ CPSourceFileRef,&
CALL timestop(0.0_dp,handle)
CONTAINS
SUBROUTINE execute_job(i)
INTEGER :: i
! change to the new working directory
CALL m_chdir(TRIM(farming_env%Job(i)%cwd),ierr)
CPPostcondition(ierr==0,cp_failure_level,routineP,error,failure)
! generate a fresh call to cp2k_run
IF (new_rank == 0) THEN
! generate the output file
WRITE(output_file,'(A12,I5.5)') "FARMING_OUT_",i
CALL parser_create(my_parser,file_name=TRIM(farming_env%Job(i)%input),error=error)
label="&GLOBAL"
CALL parser_search_string(my_parser,label,ignore_case=.TRUE.,found=found,error=error)
IF (found) THEN
CALL create_global_section(g_section,error=error)
CALL section_vals_create(g_data,g_section,error=error)
CALL cp_unit_set_create(default_units, "OUTPUT",error=error)
CALL section_vals_parse(g_data,my_parser,default_units,&
error=error)
CALL cp_unit_set_release(default_units,error=error)
CALL section_vals_val_get(g_data,"PROJECT",&
c_val=str, error=error)
IF (str.NE."") output_file=TRIM(str)//".out"
CALL section_vals_val_get(g_data,"OUTPUT_FILE_NAME",&
c_val=str,error=error)
IF (str.NE."") output_file=str
CALL section_vals_release(g_data,error=error)
CALL section_release(g_section,error=error)
END IF
CALL parser_release(my_parser,error=error)
CALL open_file(file_name=TRIM(output_file),&
file_action="WRITE",&
file_status="UNKNOWN",&
file_position="APPEND",&
unit_number=new_output_unit)
ELSE
! this should never be used by cp2k_run, but e.g. still happens, incorrectly,
! on eror, writes now to fort.999 so that error messages can be recovered and fixed
new_output_unit=999
ENDIF
run_OK=cp2k_run(TRIM(farming_env%Job(i)%input),new_output_unit,new_group)
IF (new_rank == 0) CALL close_file(unit_number=new_output_unit)
! change to the original working directory
CALL m_chdir(TRIM(farming_env%cwd),ierr)
CPPostcondition(ierr==0,cp_failure_level,routineP,error,failure)
END SUBROUTINE execute_job
END SUBROUTINE farming_run
!!****f* cp2k_runs/write_cp2k_html_manual *
@ -744,7 +937,7 @@ WRITE(unit_nr,*) ' <!-- END CODE --></TD><TD> '
WRITE(unit_nr,*) "<HTML><BODY><HEAD><TITLE>The cp2k literature list</TITLE>"
WRITE(unit_nr,*) "<H1>CP2K references</H1>"
CALL print_all_references(sorted=.FALSE.,cited_only=.FALSE., &
format=print_format_html,unit=unit_nr)
FORMAT=print_format_html,unit=unit_nr)
WRITE(unit_nr,*) '<BR><hr>Back to the <A HREF="http://cp2k.berlios.de/">CP2K homepage</A>'
WRITE(unit_nr,*) "</BODY></HTML>"
CALL close_file(unit_nr)

View file

@ -8,7 +8,8 @@ MODULE farming_methods
USE cp_output_handling, ONLY: cp_print_key_finished_output,&
cp_print_key_unit_nr
USE cp_para_types, ONLY: cp_para_env_type
USE farming_types, ONLY: farming_env_type
USE farming_types, ONLY: farming_env_type, &
init_job_type, job_pending, job_running, job_finished
USE input_section_types, ONLY: section_vals_get,&
section_vals_get_subs_vals,&
section_vals_type,&
@ -18,12 +19,80 @@ MODULE farming_methods
IMPLICIT NONE
PRIVATE
PUBLIC :: farming_parse_input
PUBLIC :: farming_parse_input, get_next_job
! must be negative in order to avoid confusion with job numbers
INTEGER, PARAMETER, PUBLIC :: do_nothing = -1, &
do_wait = -2, &
do_deadlock = -3
CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'farming_methods'
CONTAINS
SUBROUTINE get_next_job(farming_env,start,end,current,todo)
TYPE(farming_env_type), POINTER :: farming_env
INTEGER, INTENT(IN) :: start, end
INTEGER, INTENT(INOUT) :: current
INTEGER, INTENT(OUT) :: todo
INTEGER :: itry,idep,ndep,icheck
LOGICAL :: dep_ok
IF (farming_env%cycle) THEN
IF (current<start) THEN
current=start
ELSE
current=current+1
ENDIF
IF (current>end) THEN
todo=do_nothing
ELSE
todo=MODULO(current-1,farming_env%njobs)+1
ENDIF
ELSE
! find a pending job
itry=start
todo=do_nothing
DO itry=start,end
IF (farming_env%job(itry)%status==job_pending) THEN
! see if all dependencies are OK
ndep=SIZE(farming_env%job(itry)%dependencies)
dep_ok=.TRUE.
dep: DO idep=1,ndep
DO icheck=start,end
IF (farming_env%job(icheck)%status.NE.job_finished) THEN
IF (farming_env%job(icheck)%id==farming_env%job(itry)%dependencies(idep)) THEN
dep_ok=.FALSE.
EXIT dep
ENDIF
ENDIF
ENDDO
ENDDO dep
! if there are pending jobs, the slave can not be told to stop
! at least wait if there are unresolved dependencies
IF (dep_OK) THEn
todo=itry
EXIT
ELSE
todo=do_wait
ENDIF
ENDIF
ENDDO
! If we have to wait, but there are no running jobs we are deadlocked
! which we signal
IF (todo==do_wait) THEN
dep_OK=.FALSE.
DO itry=start,end
IF (farming_env%job(icheck)%status.EQ.job_running) dep_OK=.TRUE.
ENDDO
IF (.NOT. dep_OK) todo=do_deadlock
ENDIF
ENDIF
END SUBROUTINE get_next_job
SUBROUTINE farming_parse_input(farming_env,root_section,para_env,error)
TYPE(farming_env_type), POINTER :: farming_env
TYPE(section_vals_type), POINTER :: root_section
@ -35,11 +104,13 @@ CONTAINS
CHARACTER(LEN=3) :: text
INTEGER :: i, iunit, n_rep_val, &
output_unit, stat
output_unit, stat, num_slaves
INTEGER, DIMENSION(:), POINTER :: i_vals
LOGICAL :: failure, found
TYPE(cp_logger_type), POINTER :: logger
TYPE(section_vals_type), POINTER :: farming_section, jobs_section
INTEGER, POINTER, DIMENSION(:) :: dependencies
LOGICAL :: has_dep
failure=.FALSE.
@ -80,34 +151,81 @@ CONTAINS
END IF
END IF
CALL section_vals_val_get(farming_section,"restart",&
CALL section_vals_val_get(farming_section,"RESTART",&
l_val=farming_env%restart,error=error)
CALL section_vals_val_get(farming_section,"max_steps",&
CALL section_vals_val_get(farming_section,"MAX_JOBS_PER_GROUP",&
i_val=farming_env%max_steps,error=error)
CALL section_vals_val_get(farming_section,"cycle",&
CALL section_vals_val_get(farming_section,"CYCLE",&
l_val=farming_env%cycle,error=error)
CALL section_vals_val_get(farming_section,"WAIT_TIME",&
r_val=farming_env%wait_time,error=error)
CALL section_vals_val_get(farming_section,"MASTER_SLAVE",&
l_val=farming_env%master_slave,error=error)
jobs_section => section_vals_get_subs_vals(farming_section,"JOB",error=error)
CALL section_vals_get(jobs_section,n_repetition=farming_env % njobs,error=error)
ALLOCATE(farming_env%Job_cwd(farming_env % njobs),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
ALLOCATE(farming_env%Job_input(farming_env % njobs),STAT=stat)
ALLOCATE(farming_env%Job(farming_env % njobs),STAT=stat)
CPPostcondition(stat==0,cp_failure_level,routineP,error,failure)
CALL init_job_type(farming_env%job)
IF (.NOT. failure) THEN
has_dep=.FALSE.
DO i=1,farming_env%njobs
CALL section_vals_val_get(jobs_section,i_rep_section=i,&
keyword_name="directory",c_val=farming_env%Job_cwd(i),error=error)
keyword_name="DIRECTORY",c_val=farming_env%Job(i)%cwd,error=error)
CALL section_vals_val_get(jobs_section,i_rep_section=i,&
keyword_name="input_file_name",c_val=farming_env%Job_input(i),error=error)
keyword_name="INPUT_FILE_NAME",c_val=farming_env%Job(i)%input,error=error)
! if job id is not specified the job id is the index
CALL section_vals_val_get(jobs_section,i_rep_section=i,&
keyword_name="JOB_ID",n_rep_val=n_rep_val,error=error)
IF (n_rep_val==0) THEN
farming_env%Job(i)%id=i
ELSE
CALL section_vals_val_get(jobs_section,i_rep_section=i,&
keyword_name="JOB_ID",i_val=farming_env%Job(i)%id,error=error)
ENDIF
! get dependencies
CALL section_vals_val_get(jobs_section,i_rep_section=i,&
keyword_name="DEPENDENCIES",n_rep_val=n_rep_val,error=error)
IF (n_rep_val==0) THEN
ALLOCATE(farming_env%Job(i)%dependencies(0))
ELSE
CALL section_vals_val_get(jobs_section,i_rep_section=i,&
keyword_name="DEPENDENCIES",i_vals=dependencies,error=error)
ALLOCATE(farming_env%Job(i)%dependencies(SIZE(dependencies,1)))
farming_env%Job(i)%dependencies=dependencies
IF (SIZE(dependencies,1).NE.0) has_dep=.TRUE.
ENDIF
END DO
END IF
IF (has_dep) THEN
CPPostcondition(farming_env%master_slave,cp_failure_level,routineP,error,failure)
CPPostcondition(.NOT.farming_env%cycle,cp_failure_level,routineP,error,failure)
ENDIF
IF (.NOT. failure) THEN
logger => cp_error_get_logger(error)
output_unit=cp_print_key_unit_nr(logger,root_section,"GLOBAL%PROGRAM_RUN_INFO",&
extension=".log",error=error)
! master slave not supported
IF (para_env%num_pe==1) THEN
farming_env%master_slave=.FALSE.
WRITE(output_unit,FMT="(T2,A)") "FARMING| Master-slave setup not support for serial runs"
ENDIF
IF (farming_env%master_slave) THEN
num_slaves=para_env%num_pe-1
ELSE
num_slaves=para_env%num_pe
ENDIF
IF (output_unit > 0) THEN
WRITE(output_unit,FMT="(T2,A,T71,I10)") "FARMING| Number of jobs found",farming_env%njobs
IF (farming_env%ngroup_wish_set) THEN
@ -119,10 +237,10 @@ CONTAINS
WRITE(output_unit,FMT='(I4)',ADVANCE="NO") farming_env%group_partition(i)
END DO
WRITE(output_unit,*)
IF (SUM(farming_env%group_partition).NE.para_env%num_pe) THEN
IF (SUM(farming_env%group_partition).NE.num_slaves) THEN
WRITE(output_unit,FMT="(T2,A,T61,I10,T71,I10)") &
"FARMING| WARNING : partition CPUs not equal actual number ", &
para_env%num_pe, SUM(farming_env%group_partition)
"FARMING| WARNING : group partition CPUs not equal to the available number (ignoring master) ", &
num_slaves, SUM(farming_env%group_partition)
WRITE(output_unit,FMT="(T2,A)") "FARMING| partition data ignored" ! any better idea ??
DEALLOCATE(farming_env%group_partition)
ENDIF

View file

@ -4,13 +4,23 @@
!-----------------------------------------------------------------------------!
MODULE farming_types
USE kinds, ONLY: default_path_length
USE kinds, ONLY: default_path_length,dp
#include "cp_common_uses.h"
IMPLICIT NONE
PRIVATE
PUBLIC :: farming_env_type, deallocate_farming_env, init_farming_env
PUBLIC :: farming_env_type, deallocate_farming_env, init_farming_env, init_job_type, job_type
INTEGER, PUBLIC, PARAMETER :: job_pending=1,job_running=2,job_finished=3
TYPE job_type
CHARACTER(LEN=default_path_length) :: cwd ! the directory to go to
CHARACTER(LEN=default_path_length) :: input ! the input file to use
INTEGER :: ID ! the ID of this job
INTEGER, POINTER, DIMENSION(:) :: dependencies ! the dependencies of this job
INTEGER :: status ! pending,running,finished
END TYPE job_type
TYPE farming_env_type
INTEGER :: group_size_wish
@ -19,15 +29,15 @@ MODULE farming_types
LOGICAL :: ngroup_wish_set
LOGICAL :: restart
LOGICAL :: CYCLE
LOGICAL :: master_slave
INTEGER, DIMENSION(:), POINTER :: group_partition ! user preference for partitioning the cpus
CHARACTER(LEN=default_path_length) :: cwd ! directory we started from
INTEGER :: Njobs ! how many jobs to run
INTEGER :: restart_n ! where to start
INTEGER :: max_steps ! max number of steps,
! results in max_steps*Ngroup jobs being run
CHARACTER(LEN=default_path_length), DIMENSION(:), POINTER :: Job_cwd ! list of directories to go to
CHARACTER(LEN=default_path_length), DIMENSION(:), POINTER :: Job_input ! name of the input file to use
INTEGER, DIMENSION(:), POINTER :: Job_group ! number of the target group
TYPE(job_type), DIMENSION(:), POINTER :: job ! a list of jobs
REAL(KIND=dp) :: wait_time
END TYPE farming_env_type
CONTAINS
@ -64,17 +74,66 @@ SUBROUTINE init_farming_env(farming_env)
farming_env%restart = .FALSE.
farming_env%restart_n = 1
farming_env%cycle = .FALSE.
farming_env%master_slave = .FALSE.
NULLIFY(farming_env%group_partition)
farming_env%cwd = "."
farming_env%Njobs = 0
! so that maxsteps*ngroup is not overflowing
farming_env%max_steps = HUGE(0)/32768
NULLIFY(farming_env%Job_cwd)
NULLIFY(farming_env%Job_input)
NULLIFY(farming_env%Job_group)
NULLIFY(farming_env%Job)
ENDIF
END SUBROUTINE
!!****f* farming_types/init_job_type *
!!
!! NAME
!! init_job_type
!!
!! FUNCTION
!! provide a default initialization
!!
!! NOTES
!!
!! INPUTS
!!
!! MODIFICATION HISTORY
!! 09.2007 created [Joost VandeVondele ]
!!
!!*** **********************************************************************
ELEMENTAL SUBROUTINE init_job_type(job)
TYPE(job_type), INTENT(OUT) :: job
job%cwd=""
job%input=""
job%ID=-1
job%status=job_pending
NULLIFY(job%dependencies)
END SUBROUTINE init_job_type
!!****f* farming_types/deallocate_job_type *
!!
!! NAME
!! deallocate_job_type
!!
!! FUNCTION
!! deallocates all memory associated with this job
!!
!! NOTES
!!
!! INPUTS
!!
!! MODIFICATION HISTORY
!! 09.2007 created [Joost VandeVondele ]
!!
!!*** **********************************************************************
SUBROUTINE deallocate_job_type(job)
TYPE(job_type) :: job
IF (ASSOCIATED(job%dependencies)) DEALLOCATE(job%dependencies)
END SUBROUTINE deallocate_job_type
!!****f* farming_types/deallocate_farming_env *
!!
!! NAME
@ -97,17 +156,21 @@ END SUBROUTINE
!! 03.2004 created [Joost VandeVondele]
!!
!!*** **********************************************************************
SUBROUTINE deallocate_farming_env(farming_env)
TYPE(farming_env_type), POINTER :: farming_env
IF (ASSOCIATED(farming_env)) THEN
IF (ASSOCIATED(farming_env%Job_cwd)) DEALLOCATE(farming_env%Job_cwd)
IF (ASSOCIATED(farming_env%Job_input)) DEALLOCATE(farming_env%Job_input)
IF (ASSOCIATED(farming_env%Job_group)) DEALLOCATE(farming_env%Job_group)
IF (ASSOCIATED(farming_env%group_partition)) DEALLOCATE(farming_env%group_partition)
DEALLOCATE(farming_env) ! and the type itself
ENDIF
INTEGER :: I
IF (ASSOCIATED(farming_env)) THEN
IF (ASSOCIATED(farming_env%job)) THEN
DO I=1,SIZE(farming_env%job,1)
CALL deallocate_job_type(farming_env%job(I))
ENDDO
DEALLOCATE(farming_env%job)
ENDIF
IF (ASSOCIATED(farming_env%group_partition)) DEALLOCATE(farming_env%group_partition)
DEALLOCATE(farming_env) ! and the type itself
ENDIF
END SUBROUTINE deallocate_farming_env
END MODULE farming_types
!!*****

View file

@ -1269,18 +1269,27 @@ SUBROUTINE create_farming_section(section,error)
repeats=.FALSE., required=.TRUE., error=error)
NULLIFY(keyword)
CALL keyword_create(keyword, name="GROUP_SIZE",&
description="Gives the wanted size of a working group.",&
usage="group_size 2", default_i_val=8, error=error)
CALL keyword_create(keyword, name="MASTER_SLAVE",&
description="If a master-slave setup should be employed, in which one process is used to distribute the tasks. "//&
"This is most useful to load-balance if not all jobs have the same length, "//&
"and a lot of CPUs/groups are availabe.",&
usage="MASTER_SLAVE",default_l_val=.FALSE.,lone_keyword_l_val=.TRUE.,error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="NGROUPS",variants=(/"NGROUP"/),&
description="Gives the wanted number of groups.",&
description="Gives the preferred number of working groups.",&
usage="ngroups 4", type_of_var=integer_t, error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="GROUP_SIZE",&
description="Gives the preferred size of a working group, "//&
"groups will always be equal or larger than this size.",&
usage="group_size 2", default_i_val=8, error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="GROUP_PARTITION",&
description="gives the exact number of processors for each group.",&
usage="group_partition 2 2 4 2 4 ", type_of_var=integer_t, n_var=-1, error=error)
@ -1288,44 +1297,75 @@ SUBROUTINE create_farming_section(section,error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="RESTART",&
description="if you are restarting a farming job (and should pick up where the previous left off)",&
description="restart a farming job (and should pick up where the previous left off)",&
usage="RESTART",default_l_val=.FALSE.,lone_keyword_l_val=.TRUE.,&
error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="MAX_STEP",variants=(/"max_steps"/),&
description="maximum number of steps done by a processor",&
CALL keyword_create(keyword, name="MAX_JOBS_PER_GROUP",&
variants=(/"MAX_JOBS"/),&
description="maximum number of jobs executed per group",&
usage="max_step 4", default_i_val=HUGE(0)/32768, error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="CYCLE",&
description="If farming should cycle between the inputs" ,&
description="If farming should process all jobs in a cyclic way, stopping only if MAX_JOBS_PER_GROUP is exceeded." ,&
usage="CYCLE",default_l_val=.FALSE.,lone_keyword_l_val=.TRUE.,&
error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword, name="WAIT_TIME",&
description="Time to wait [s] for a new task if no task is currently available, make this zero if no clock is available",&
usage="WAIT_TIME 0.1",default_r_val=0.5_dp, error=error)
CALL section_add_keyword(section,keyword,error=error)
CALL keyword_release(keyword,error=error)
NULLIFY(sub_section)
CALL section_create(sub_section,name="job",&
CALL section_create(sub_section,name="JOB",&
description="description of the jobs to be executed",&
repeats=.TRUE., required=.TRUE., error=error)
CALL keyword_create(keyword,name="directory",&
CALL keyword_create(keyword,name="DIRECTORY",&
description="the directory in which the job should be executed",&
usage="directory /my/path",&
usage="DIRECTORY /my/path",&
required=.TRUE.,default_lc_val=".",error=error)
CALL section_add_keyword(sub_section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword,name="input_file_name",&
CALL keyword_create(keyword,name="INPUT_FILE_NAME",&
description="the filename of the input file",&
usage="input_file my_input.inp",&
required=.TRUE.,default_lc_val="input.inp",error=error)
CALL section_add_keyword(sub_section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword,name="JOB_ID",&
description="An ID used to indentify a job in DEPENDENCIES. "//&
"JOB_IDs do not need to be unique, dependencies will be on all jobs with a given ID. "//&
"If no JOB_ID is given, the index of the &JOB section in the input file will be used. ",&
usage="JOB_ID 13", type_of_var=integer_t, error=error)
CALL section_add_keyword(sub_section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL keyword_create(keyword,name="DEPENDENCIES",&
description="specifies a list of JOB_IDs on which the current job depends. "//&
"The current job will not be executed before all the dependencies have finished. "//&
"The keyword requires a MASTER_SLAVE farming run. "//&
"Beyond the default case, some special cases might arise: "//&
"1) circular dependencies will lead to a deadlock. "//&
"2) This keyword is not compatible with CYCLE. "//&
"3) MAX_JOBS_PER_GROUP is ignored (though only a total of MAX_JOBS_PER_GROUP*NGROUPS jobs will be executed) "//&
"4) dependencies on jobs that will not be executed (due to RESTART or MAX_JOBS_PER_GROUP) are ignored. "//&
"Additionally, note that, on some file systems, "//&
" output (restart) files might not be immediately available on all compute nodes,"//&
"potentially resulting in unexpected failures.", &
usage="DEPENDENCIES 13 1 7",type_of_var=integer_t, n_var=-1, error=error)
CALL section_add_keyword(sub_section,keyword,error=error)
CALL keyword_release(keyword,error=error)
CALL section_add_subsection(section, sub_section, error=error)
CALL section_release(sub_section,error=error)

View file

@ -54,12 +54,19 @@ MODULE message_passing
#ifdef __parallel
LOGICAL, PARAMETER :: cp2k_is_parallel=.TRUE.
INTEGER, PARAMETER, PUBLIC :: mp_any_tag=MPI_ANY_TAG
INTEGER, PARAMETER, PUBLIC :: mp_any_source=MPI_ANY_SOURCE
#else
LOGICAL, PARAMETER :: cp2k_is_parallel=.FALSE.
INTEGER, PARAMETER, PUBLIC :: mp_any_tag=-1
INTEGER, PARAMETER, PUBLIC :: mp_any_source=-2
#endif
CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'message_passing'
! parameters that might be needed
! init and error
PUBLIC :: mp_world_init, mp_world_finalize
PUBLIC :: mp_stop, mp_abort
@ -84,6 +91,7 @@ MODULE message_passing
PUBLIC :: mp_sum_scatter, mp_shift, mp_isendrecv, mp_wait, mp_waitall
PUBLIC :: mp_gatherv
PUBLIC :: mp_sum_i8
PUBLIC :: mp_send, mp_recv
! currently not used
! PUBLIC :: mp_group
@ -161,6 +169,14 @@ MODULE message_passing
mp_alltoall_i11v, mp_alltoall_r11v
END INTERFACE
INTERFACE mp_send
MODULE PROCEDURE mp_send_i,mp_send_iv
END INTERFACE
INTERFACE mp_recv
MODULE PROCEDURE mp_recv_i,mp_recv_iv
END INTERFACE
INTERFACE mp_sendrecv
MODULE PROCEDURE mp_sendrecv_rm3, mp_sendrecv_rm2, mp_sendrecv_rv
END INTERFACE
@ -185,7 +201,7 @@ MODULE message_passing
REAL (KIND=dp) :: time
END TYPE mp_perf_type
INTEGER, PARAMETER :: MAX_PERF = 12
INTEGER, PARAMETER :: MAX_PERF = 14
TYPE mp_perf_env_type
!private
@ -207,7 +223,8 @@ MODULE message_passing
(/"MP_Group ", "MP_Bcast ", "MP_Allreduce ", &
"MP_Gather ", "MP_Sync ", "MP_Alltoall ", &
"MP_SendRecv ", "MP_ISendRecv ", "MP_Wait ", &
"MP_comm_split ", "MP_ISend ", "MP_IRecv "/)
"MP_comm_split ", "MP_ISend ", "MP_IRecv ", &
"MP_Send ", "MP_Recv "/)
REAL (KIND=dp) :: t_start, t_end
! we make some assumptions on the length of INTEGERS, REALS and LOGICALS
@ -1456,6 +1473,123 @@ END SUBROUTINE mp_alltoall_i
!******************************************************************************
! ..mp_send
SUBROUTINE mp_send_iv(msg,dest,tag,gid)
INTEGER :: msg( : ), dest, gid,tag
INTEGER :: ierr, msglen
INTEGER :: handle
CHARACTER(len=*), PARAMETER :: routineN = 'mp_send_iv', &
routineP = moduleN//':'//routineN
#if defined(__mp_timeset__)
CALL timeset(routineN,handle)
#endif
ierr = 0
msglen = SIZE(msg)
#if defined(__parallel)
t_start = m_walltime ( )
CALL mpi_send(msg,msglen,MPI_INTEGER,dest,tag,gid,ierr)
IF ( ierr /= 0 ) CALL mp_stop( ierr, "mpi_send @ mp_send_iv" )
t_end = m_walltime ( )
CALL add_perf(perf_id=13,count=1,time=t_end-t_start,msg_size=msglen*intlen)
#endif
#if defined(__mp_timeset__)
CALL timestop(handle)
#endif
END SUBROUTINE mp_send_iv
SUBROUTINE mp_send_i(msg,dest,tag,gid)
INTEGER :: msg, dest, gid,tag
INTEGER :: ierr, msglen
INTEGER :: handle
CHARACTER(len=*), PARAMETER :: routineN = 'mp_send_iv', &
routineP = moduleN//':'//routineN
#if defined(__mp_timeset__)
CALL timeset(routineN,handle)
#endif
ierr = 0
msglen = 1
#if defined(__parallel)
t_start = m_walltime ( )
CALL mpi_send(msg,msglen,MPI_INTEGER,dest,tag,gid,ierr)
IF ( ierr /= 0 ) CALL mp_stop( ierr, "mpi_send @ mp_send_iv" )
t_end = m_walltime ( )
CALL add_perf(perf_id=13,count=1,time=t_end-t_start,msg_size=msglen*intlen)
#endif
#if defined(__mp_timeset__)
CALL timestop(handle)
#endif
END SUBROUTINE mp_send_i
! ..mp_recv
SUBROUTINE mp_recv_iv(msg,source,tag,gid)
INTEGER, INTENT(INOUT) :: msg( : ), source, tag
INTEGER, INTENT(IN) :: gid
INTEGER :: ierr, msglen
INTEGER :: handle
INTEGER, DIMENSION(:), ALLOCATABLE :: status
CHARACTER(len=*), PARAMETER :: routineN = 'mp_recv_iv', &
routineP = moduleN//':'//routineN
#if defined(__mp_timeset__)
CALL timeset(routineN,handle)
#endif
ierr = 0
msglen = SIZE(msg)
#if defined(__parallel)
ALLOCATE(status(MPI_STATUS_SIZE))
t_start = m_walltime ( )
CALL mpi_recv(msg,msglen,MPI_INTEGER,source,tag,gid,status,ierr)
IF ( ierr /= 0 ) CALL mp_stop( ierr, "mpi_recv @ mp_recv_iv" )
t_end = m_walltime ( )
CALL add_perf(perf_id=14,count=1,time=t_end-t_start,msg_size=msglen*intlen)
source = status(MPI_SOURCE)
tag = status(MPI_TAG)
DEALLOCATE(status)
#endif
#if defined(__mp_timeset__)
CALL timestop(handle)
#endif
END SUBROUTINE mp_recv_iv
SUBROUTINE mp_recv_i(msg,source,tag,gid)
INTEGER, INTENT(INOUT) :: msg, source, tag
INTEGER, INTENT(IN) :: gid
INTEGER :: ierr, msglen
INTEGER :: handle
INTEGER, DIMENSION(:), ALLOCATABLE :: status
CHARACTER(len=*), PARAMETER :: routineN = 'mp_recv_iv', &
routineP = moduleN//':'//routineN
#if defined(__mp_timeset__)
CALL timeset(routineN,handle)
#endif
ierr = 0
msglen = 1
#if defined(__parallel)
ALLOCATE(status(MPI_STATUS_SIZE))
t_start = m_walltime ( )
CALL mpi_recv(msg,msglen,MPI_INTEGER,source,tag,gid,status,ierr)
IF ( ierr /= 0 ) CALL mp_stop( ierr, "mpi_recv @ mp_recv_iv" )
t_end = m_walltime ( )
CALL add_perf(perf_id=14,count=1,time=t_end-t_start,msg_size=msglen*intlen)
source = status(MPI_SOURCE)
tag = status(MPI_TAG)
DEALLOCATE(status)
#endif
#if defined(__mp_timeset__)
CALL timestop(handle)
#endif
END SUBROUTINE mp_recv_i
!..mp_bcast
SUBROUTINE mp_bcast_i1(msg,source,gid)
INTEGER :: msg, source, gid

View file

@ -0,0 +1,13 @@
# runs are executed in the same order as in this file
# the second field tells which test should be run in order to compare with the last available output
# e.g. 0 means do not compare anything, running is enough
# 1 compares the last total energy in the file
# for details see cp2k/tools/do_regtest
farming-1.inp 0
farming-2.inp 0
farming-3.inp 0
farming-4.inp 0
farming-5.inp 0
farming-6.inp 0
farming-7.inp 0
farming-8.inp 0

View file

View file

@ -0,0 +1,45 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUP 1
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,45 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUP 3
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,45 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUP 2
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,45 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
GROUP_SIZE 4
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,47 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUP 2
CYCLE
MAX_JOBS_PER_GROUP 10
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,48 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUP 2
! CYCLE
RESTART
! MAX_JOBS_PER_GROUP 10
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,46 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUPS 2
MASTER_SLAVE
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
&END JOB
&END FARMING

View file

@ -0,0 +1,62 @@
&GLOBAL
PROGRAM FARMING
RUN_TYPE NONE
&END GLOBAL
&FARMING
NGROUPS 5
MASTER_SLAVE
&JOB
DIRECTORY dir-1
INPUT_FILE_NAME water.inp
JOB_ID 1
&END JOB
&JOB
DIRECTORY dir-2
INPUT_FILE_NAME water.inp
JOB_ID 1
&END JOB
&JOB
DIRECTORY dir-3
INPUT_FILE_NAME water.inp
JOB_ID 2
DEPENDENCIES 1
&END JOB
&JOB
DIRECTORY dir-4
INPUT_FILE_NAME water.inp
JOB_ID 3
DEPENDENCIES 1 2
&END JOB
&JOB
DIRECTORY dir-5
INPUT_FILE_NAME water.inp
JOB_ID 4
DEPENDENCIES 5
&END JOB
&JOB
DIRECTORY dir-6
INPUT_FILE_NAME water.inp
JOB_ID 5
DEPENDENCIES 3
&END JOB
&JOB
DIRECTORY dir-7
INPUT_FILE_NAME water.inp
JOB_ID 5
DEPENDENCIES 3
&END JOB
&JOB
DIRECTORY dir-8
INPUT_FILE_NAME water.inp
JOB_ID 5
DEPENDENCIES 3
&END JOB
&JOB
DIRECTORY dir-9
INPUT_FILE_NAME water.inp
JOB_ID 5
DEPENDENCIES 3
&END JOB
&END FARMING

18
tests/FARMING/regtest-1/tidy Executable file
View file

@ -0,0 +1,18 @@
#!/bin/tcsh
# Script to clean files after execution of a test
echo "This script will clean the directory" $PWD "from scratch files.."
echo
foreach file (`cvs update -A -P|grep '? '|awk '{print $2}'`)
if ( ${file:e} == 'inp' ) then
echo "This files could be a new input file you don't want to be deleted.. Do you really want it deleted?"
if ( $1 == "-int" ) then
echo "You requested to run the cleaning script interactively! Forget about complains!"
rm -f $file
else
rm -i $file
endif
else
rm -f $file
endif
end
rm -f *.bak

18
tests/FARMING/water.pot Normal file
View file

@ -0,0 +1,18 @@
BONDS
HT HT 0.000 1.5139
OT HT 450.000 0.9572
ANGLES
HT OT HT 55.000 104.5200
DIHEDRALS
IMPROPER
NONBONDED
HT 0.000000 -0.046000 0.224500
OT 0.000000 -0.152100 1.768200
HBOND CUTHB 0.5
END

View file

@ -0,0 +1,3 @@
ATOM 1 OT TIP3 1 -0.757 -5.616 -7.101 1.00 0.00 WAT O
ATOM 2 HT TIP3 1 -1.206 -5.714 -6.262 1.00 0.00 WAT H
ATOM 3 HT TIP3 1 0.024 -5.102 -6.896 1.00 0.00 WAT H

View file

@ -56,3 +56,4 @@ FE/regtest-2
QS/regtest-ot-refine-2
QS/regtest-nonortho
QS/regtest-ot-refine-3
FARMING/regtest-1