ecmwf-hpc2020.cfg.tmpl 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. # Platform dependent configuration functions for the 'HPC2020' cluster
  2. # (ECMWF, BOLOGNA)
  3. function configure()
  4. {
  5. # This function should configure all settings/modules needed to
  6. # later prepare the EC-Earth run directory and set variables used
  7. # in the run script
  8. # Configure paths for building/running EC-Earth
  9. ecearth_src_dir=[[[PLT:ACTIVE:ECEARTH_SRC_DIR]]]
  10. run_dir=[[[PLT:ACTIVE:RUN_DIR]]]
  11. ini_data_dir=[[[PLT:ACTIVE:INI_DATA_DIR]]]
  12. # File for standard output.
  13. # NOTE: This will be modified for restart jobs!
  14. stdout_file=${start_dir}/out/[[[MOD:GENERAL:EXP_NAME]]].$$.out
  15. # Resubmit this job for automatic restarts? [true/false]
  16. # Also, add options for the resubmit command here.
  17. resubmit_job=[[[PLT:ACTIVE:RESUBMIT_JOB]]]
  18. resubmit_opt="[[[PLT:ACTIVE:RESUBMIT_OPT]]]"
  19. # Configure grib api paths
  20. export ECCODES_DEFINITION_PATH=[[[PLT:ACTIVE:ECEARTH_SRC_DIR]]]/util/grib_table_126:[[[PLT:ACTIVE:GRIBAPI_BASE_DIR]]]/[[[PLT:ACTIVE:GRIBAPI_DEFINITION_SUBDIR]]]
  21. export ECCODES_SAMPLES_PATH=[[[PLT:ACTIVE:GRIBAPI_BASE_DIR]]]/[[[PLT:ACTIVE:GRIBAPI_SAMPLES_SUBDIR]]]
  22. export GRIB_BIN_PATH=[[[PLT:ACTIVE:GRIBAPI_BASE_DIR]]]/[[[PLT:ACTIVE:GRIBAPI_BIN_SUBDIR]]]
  23. # Configure number of processors per node
  24. proc_per_node=[[[PLT:ACTIVE:PROC_PER_NODE]]]
  25. # Configure and load modules
  26. pre_load_modules_cmd="[[[PLT:ACTIVE:PRE_LOAD_MODULES_CMD]]]"
  27. module_list="[[[PLT:ACTIVE:MODULE_LIST]]]"
  28. if [ -n "${module_list}" ]
  29. then
  30. set +u
  31. if [ -n "${pre_load_modules_cmd}" ]
  32. then
  33. ${pre_load_modules_cmd}
  34. fi
  35. for m in "${module_list}"
  36. do
  37. module load $m
  38. done
  39. set -u
  40. fi
  41. # Add directories to the shared library search path
  42. if [ -n "[[[PLT:ACTIVE:ADD_TO_LD_LIBRARY_PATH]]]" ]
  43. then
  44. export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"[[[PLT:ACTIVE:ADD_TO_LD_LIBRARY_PATH]]]"
  45. fi
  46. # Use machinefiles or not
  47. [[ `echo "$use_machinefile" | tr '[:upper:]' '[:lower:]'` == true ]] && use_machinefile=true || use_machinefile=false
  48. export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
  49. ulimit -s unlimited
  50. ulimit -n 2048
  51. ulimit -c unlimited
  52. ulimit -a
  53. }
  54. machinefile_config()
  55. {
  56. # LPJG is memory hungry (limit is somewhere between 24 and 32 cores per node)
  57. lpjg_exc=true
  58. lpjg_maxppn=10
  59. # Let only nemo/xios/runoff_mapper share nodes:
  60. tm5_exc=true
  61. ifs_exc=true
  62. # # Enforced C-cycle configurations
  63. # if has_config ifs nemo pisces rnfmapper xios lpjg
  64. # then
  65. # if ! has_config tm5
  66. # then
  67. # # c-driven
  68. # ...
  69. # else
  70. # # e-driven
  71. # ...
  72. # fi
  73. # fi
  74. }
  75. machinefile_init()
  76. {
  77. # Get max processes per node from the platform variable
  78. max_ppn=$proc_per_node
  79. components=( ifs nem xio rnf amip lpjg tm5 )
  80. # Default to sharing nodes and to using between 1 and all procs on a node
  81. for component in ${components[@]}
  82. do
  83. eval ${component}_exc=false
  84. eval ${component}_minppn=1
  85. eval ${component}_maxppn=$max_ppn
  86. done
  87. # Call user configuration
  88. machinefile_config
  89. # List of hosts allocated for the current job
  90. hosts=(`scontrol show hostname | paste -s`)
  91. nhosts=${#hosts[@]}
  92. nhosts0=$(( ${#hosts[@]} - 1 ))
  93. hosts[$nhosts]=dummy
  94. # Array to store processes as they are assigned
  95. declare -a -g processes_hosts
  96. declare -Ag has_exclusive
  97. for n in `seq 0 $nhosts`
  98. do
  99. processes_hosts[$n]=0
  100. has_exclusive[${hosts[$n]}]=false
  101. done
  102. > machinefile
  103. }
  104. find_empty_node()
  105. {
  106. current_hostid=0
  107. while (( ${processes_hosts[$current_hostid]} > 0 ))
  108. do
  109. (( current_hostid += 1 ))
  110. done
  111. }
  112. find_unfilled_shared_node()
  113. {
  114. current_hostid=0
  115. h=${hosts[$current_hostid]}
  116. while (( ${processes_hosts[$current_hostid]} + ${!minppn} > $max_ppn )) || ${has_exclusive[$h]}
  117. do
  118. (( current_hostid += 1 ))
  119. h=${hosts[$current_hostid]}
  120. done
  121. }
  122. machinefile_add()
  123. {
  124. total_proc=$2
  125. # Iterate through all the possible binaries
  126. for component in ${components[@]}
  127. do
  128. binary="${component}_exe_file"
  129. exclusive="${component}_exc"
  130. minppn="${component}_minppn"
  131. maxppn="${component}_maxppn"
  132. # Check if the current binary matches the input executable
  133. if [ ./$(basename ${!binary}) = "$1" ]
  134. then
  135. # Allocate up to maxppn cores in each of subsequent nodes till there are no more process to assign
  136. while [ ${total_proc} -gt 0 ]
  137. do
  138. ${!exclusive} && find_empty_node || find_unfilled_shared_node
  139. [[ ${current_hostid} -gt $nhosts0 ]] && error "Not enough computing nodes"
  140. current_hostname=${hosts[$current_hostid]}
  141. nodecount=${processes_hosts[$current_hostid]}
  142. modelcount=0
  143. ${!exclusive} && has_exclusive[$current_hostname]=true
  144. while [[ ${total_proc} -gt 0 && $modelcount -lt ${!maxppn} && $nodecount -lt $max_ppn ]]
  145. do
  146. echo ${hosts[$current_hostid]} >> machinefile
  147. (( modelcount += 1 ))
  148. (( nodecount += 1 ))
  149. let "processes_hosts[$current_hostid] += 1"
  150. let "total_proc -= 1" || true
  151. done
  152. done
  153. fi
  154. done
  155. }
  156. check_used_nodes()
  157. {
  158. local id nnodes
  159. nnodes=0
  160. for id in `seq 0 $nhosts0`
  161. do
  162. (( ${processes_hosts[$id]} > 0 )) && (( nnodes+=1 ))
  163. done
  164. if (( $SLURM_NNODES > $nnodes ))
  165. then
  166. error "Too many NODES allocated, resubmit with only $nnodes nodes"
  167. fi
  168. }
  169. launch()
  170. {
  171. # version using srun with or without machinefile
  172. if $use_machinefile
  173. then
  174. machinefile_init
  175. export SLURM_HOSTFILE=machinefile
  176. fi
  177. rm -f conf.txt
  178. _task1=-1
  179. NBTASKS=0
  180. while (( "$#" ))
  181. do
  182. nranks=$1
  183. executable=./$(basename $2)
  184. shift
  185. shift
  186. $use_machinefile && machinefile_add $executable $nranks
  187. _task0=$((_task1+1))
  188. _task1=$((_task0+nranks-1))
  189. cmd="${_task0}-${_task1} ${executable}"
  190. NBTASKS=$((NBTASKS+nranks))
  191. while (( "$#" )) && [ "$1" != "--" ]
  192. do
  193. cmd+=" $1"
  194. shift
  195. done
  196. echo ${cmd} >>conf.txt
  197. shift || true
  198. done
  199. echo '-------A-conf.txt------'
  200. cat conf.txt
  201. echo '-------E-conf.txt------'
  202. if $use_machinefile
  203. then
  204. echo '-------A-machinefile------'
  205. cat machinefile
  206. echo '-------E-machinefile------'
  207. check_used_nodes
  208. fi
  209. cmd="srun --kill-on-bad-exit=1"
  210. CONF_FILE=conf.txt
  211. echo "$cmd --ntasks=$NBTASKS -l --multi-prog $CONF_FILE"
  212. /usr/bin/time $cmd --ntasks=$NBTASKS -l --multi-prog $CONF_FILE
  213. }
  214. finalise()
  215. {
  216. # This function should execute of any post run functionality, e.g.
  217. # platform dependent cleaning or a resubmit
  218. if ${resubmit_job} && [ $(date -d "${leg_end_date}" +%s) -lt $(date -d "${run_end_date}" +%s) ]
  219. then
  220. info "Resubmitting job for leg $((leg_number+1))"
  221. # Need to go to start_dir to find the run script
  222. cd ${start_dir}
  223. mkdir -p out
  224. unset SLURM_HOSTFILE
  225. # ReSubmit the same script, overwritting only the log filename.
  226. # Note: This does not work if you specify a job name with sbatch -J jobname!
  227. sbatch \
  228. -o out/${exp_name}.$(printf %03d $((leg_number+1))) \
  229. -e out/${exp_name}.$(printf %03d $((leg_number+1))) \
  230. ${resubmit_opt} \
  231. ./${SLURM_JOB_NAME}
  232. fi
  233. }
  234. postprocess()
  235. {
  236. local cwd=$PWD
  237. local islast=$(( $(date -ud "${leg_end_date}" +%s) == $(date -ud "${run_end_date}" +%s) ))
  238. local mess prev_leg_nb model
  239. # Trigger the cmorization of the previous leg and current leg if this is the last one.
  240. cd WHERE-SCRIPT-TO-TRIGGER-ECE2CMOR3-RESIDES #ADAPT
  241. if (( leg_number > 1 )) || (( islast ))
  242. then
  243. prev_leg_nb=$(( leg_number - 1 ))
  244. for model in ifs nemo tm5 lpjg
  245. do
  246. if [ ${prev_leg_nb} -gt 0 ]
  247. then
  248. mess=${exp_name}-${model}-$(printf %03d ${prev_leg_nb})
  249. info "Submit cmorization $mess"
  250. sbatch --output=log/${exp_name}/${mess}.out --job-name=$mess SUBCMORSCRIPT.sh ${exp_name} ${model} ${prev_leg_nb} #ADAPT
  251. fi
  252. if (( islast ))
  253. then
  254. mess=${exp_name}-${model}-$(printf %03d ${leg_number})
  255. info "Submit cmorization $mess"
  256. sbatch --output=log/${exp_name}/${mess}.out --job-name=$mess SUBCMORSCRIPT.sh ${exp_name} ${model} ${leg_number} #ADAPT
  257. fi
  258. done
  259. fi
  260. cd ${cwd}
  261. }