diff --git a/src/cp2k_runs.F b/src/cp2k_runs.F
index 038acdbfb3..5402e9cadd 100644
--- a/src/cp2k_runs.F
+++ b/src/cp2k_runs.F
@@ -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,*) '
'
WRITE(unit_nr,*) "The cp2k literature list"
WRITE(unit_nr,*) "CP2K references"
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,*) '
Back to the CP2K homepage'
WRITE(unit_nr,*) ""
CALL close_file(unit_nr)
diff --git a/src/farming_methods.F b/src/farming_methods.F
index 9745eb3697..df0aa82031 100644
--- a/src/farming_methods.F
+++ b/src/farming_methods.F
@@ -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 (currentend) 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
diff --git a/src/farming_types.F b/src/farming_types.F
index 5d5946b492..4c17698cab 100644
--- a/src/farming_types.F
+++ b/src/farming_types.F
@@ -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
!!*****
diff --git a/src/input_cp2k.F b/src/input_cp2k.F
index 1353d45517..d1ec1f36a4 100644
--- a/src/input_cp2k.F
+++ b/src/input_cp2k.F
@@ -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)
diff --git a/src/message_passing.F b/src/message_passing.F
index 68fd9bcfa5..27f44e78ce 100644
--- a/src/message_passing.F
+++ b/src/message_passing.F
@@ -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
diff --git a/tests/FARMING/regtest-1/TEST_FILES b/tests/FARMING/regtest-1/TEST_FILES
new file mode 100644
index 0000000000..30efce77ec
--- /dev/null
+++ b/tests/FARMING/regtest-1/TEST_FILES
@@ -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
diff --git a/tests/FARMING/regtest-1/TEST_FILES_RESET b/tests/FARMING/regtest-1/TEST_FILES_RESET
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/FARMING/regtest-1/farming-1.inp b/tests/FARMING/regtest-1/farming-1.inp
new file mode 100644
index 0000000000..270f55f99a
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-1.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-2.inp b/tests/FARMING/regtest-1/farming-2.inp
new file mode 100644
index 0000000000..b8b6c5d556
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-2.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-3.inp b/tests/FARMING/regtest-1/farming-3.inp
new file mode 100644
index 0000000000..b601d0cf54
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-3.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-4.inp b/tests/FARMING/regtest-1/farming-4.inp
new file mode 100644
index 0000000000..97933f173a
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-4.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-5.inp b/tests/FARMING/regtest-1/farming-5.inp
new file mode 100644
index 0000000000..f1f129c771
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-5.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-6.inp b/tests/FARMING/regtest-1/farming-6.inp
new file mode 100644
index 0000000000..a0b9c584f5
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-6.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-7.inp b/tests/FARMING/regtest-1/farming-7.inp
new file mode 100644
index 0000000000..0e7f648b04
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-7.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/farming-8.inp b/tests/FARMING/regtest-1/farming-8.inp
new file mode 100644
index 0000000000..8984c31898
--- /dev/null
+++ b/tests/FARMING/regtest-1/farming-8.inp
@@ -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
+
diff --git a/tests/FARMING/regtest-1/tidy b/tests/FARMING/regtest-1/tidy
new file mode 100755
index 0000000000..8e44cbdbc5
--- /dev/null
+++ b/tests/FARMING/regtest-1/tidy
@@ -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
diff --git a/tests/FARMING/water.pot b/tests/FARMING/water.pot
new file mode 100644
index 0000000000..515c657e44
--- /dev/null
+++ b/tests/FARMING/water.pot
@@ -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
diff --git a/tests/FARMING/water_1.pdb b/tests/FARMING/water_1.pdb
new file mode 100644
index 0000000000..805520b78b
--- /dev/null
+++ b/tests/FARMING/water_1.pdb
@@ -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
diff --git a/tests/TEST_DIRS b/tests/TEST_DIRS
index c1ae607f07..0793ab8661 100644
--- a/tests/TEST_DIRS
+++ b/tests/TEST_DIRS
@@ -56,3 +56,4 @@ FE/regtest-2
QS/regtest-ot-refine-2
QS/regtest-nonortho
QS/regtest-ot-refine-3
+FARMING/regtest-1
|