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