| 1 | = Utilisation du système de batch HT-Condor = |
| 2 | |
| 3 | == Machines pour la soumission de tâches == |
| 4 | |
| 5 | Quatre machines de soumission sont à votre disposition pour soumettre vos tâches avec le système de batch HTCondor: |
| 6 | - lpsc-batch-almalinux et lpsc-batch-almalinux2 (!AlmaLinux release 9.2) |
| 7 | - lpsc-batch-fedora (Fedora 39) |
| 8 | - lpsc-batch-centos7 (CentOS Linux release 7.4) |
| 9 | |
| 10 | == Prérequis pour soumettre une tâche == |
| 11 | |
| 12 | Pour soumettre une tâche sur HTCondor il faut : |
| 13 | - un *exécutable* qui pourra dans un premier temps être testé en interactif sur les machines de soumission |
| 14 | - un *fichier de description* qui sera lu par le système HTCondor et qui définira les ressources et le type d'environnement dont a besoin votre exécutable. |
| 15 | |
| 16 | == Commandes utiles == |
| 17 | |
| 18 | - [xxx@lpsc-cyy] condor_submit <description.submit> : soumet une tâche décrite par un fichier <description.submit> |
| 19 | - [xxx@lpsc-cyy] condor_q (-long)? (-run)? : Voir l'avancement de vos tâches soumises |
| 20 | - [xxx@lpsc-cyy] condor_rm -all : Supprime toutes vos tâches de la file d'attente |
| 21 | - [xxx@lpsc-cyy] condor_status -avail : Voir toutes les machines disponibles dans le batch |
| 22 | - [xxx@lpsc-cyy] condor_status -avail : Voir toutes les machines disponibles dans le batch |
| 23 | - [xxx@lpsc-cyy] condor_status (-long)? (<machine>)? : Voir tous les attributs "!ClassAd" de l'ensemble des machines ou d'une machine donnée. (voir [https://htcondor.readthedocs.io/en/latest/users-manual/matchmaking-with-classads.html documentation]). |
| 24 | Cela permets de sélectionner les machines avec les ressources nécessaires pour exécuter une tâche en ajoutant une ligne "+Requirements" dans un fichier de soumission (voir exemples ci-dessous) |
| 25 | |
| 26 | == Liens utiles == |
| 27 | - https://htcondor.readthedocs.io/en/latest/ |
| 28 | - http://www.iac.es/sieinvens/siepedia/pmwiki.php?n=HOWTOs.CondorHowTo#howto_nestloop |
| 29 | - https://www-auth.cs.wisc.edu/lists/htcondor-users/2016-August/msg00034.shtml |
| 30 | - http://www.iac.es/sieinvens/siepedia/pmwiki.php?n=HOWTOs.CondorSubmitFile |
| 31 | |
| 32 | == Le concept d'univers == |
| 33 | |
| 34 | Un [https://htcondor.readthedocs.io/en/23.0/users-manual/choosing-an-htcondor-universe.html univers pour HTCondor] est un environnement d’exécution pour une tâche. |
| 35 | HTCondor définit plusieurs univers, plusieurs d'entre-eux ont été configurés sur la ferme de calcul du LPSC : |
| 36 | |
| 37 | - [#Universvanilla vanilla], l'univers "par défaut" avec lequel vous pouvez soumettre la plupart de vos tâches |
| 38 | - [#Universparallel parallel], l'univers qui prend en charge les tâches qui s'étendent sur plusieurs machines, où les multiples processus d'une tâche s'exécutent simultanément, éventuellement en communiquant entre eux. |
| 39 | - [#Universdocker docker], l'univers qui permet d'utiliser des containers docker depuis [https://hub.docker.com/ DockerHub] pour faire s’exécuter vos tâches |
| 40 | - [#Universjava java], l'univers pour les tâches à base d'exécutables Java |
| 41 | |
| 42 | D'autres univers existent et pourront être proposés au besoin. |
| 43 | |
| 44 | === Univers vanilla === |
| 45 | |
| 46 | ==== Python ==== |
| 47 | |
| 48 | **test_python.submit** |
| 49 | |
| 50 | {{{ |
| 51 | |
| 52 | # Définition de l'univers |
| 53 | universe = vanilla |
| 54 | |
| 55 | # Chemin vers l'exécutable |
| 56 | executable = ./test_python.py |
| 57 | |
| 58 | # Fichiers à produire en sortie |
| 59 | output = test_python.out |
| 60 | log = test_python.log |
| 61 | error = test_python.err |
| 62 | |
| 63 | # directive pour le transfert des fichiers de sortie depuis le nœud d’exécution |
| 64 | should_transfer_files = YES |
| 65 | when_to_transfer_output = ON_EXIT |
| 66 | |
| 67 | queue |
| 68 | }}} |
| 69 | |
| 70 | **test_python.py** |
| 71 | |
| 72 | {{{ |
| 73 | #!/usr/bin/env python3 |
| 74 | |
| 75 | import platform |
| 76 | from datetime import datetime |
| 77 | |
| 78 | def afficher_informations_systeme(): |
| 79 | # Obtenir le nom de la machine |
| 80 | nom_machine = platform.node() |
| 81 | |
| 82 | # Obtenir la date et l'heure actuelles |
| 83 | date_heure_actuelles = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
| 84 | |
| 85 | # Afficher les informations |
| 86 | print(f"Nom de la machine: {nom_machine}") |
| 87 | print(f"Date et heure actuelles: {date_heure_actuelles}") |
| 88 | |
| 89 | # Appeler la fonction pour afficher les informations |
| 90 | afficher_informations_systeme() |
| 91 | |
| 92 | }}} |
| 93 | |
| 94 | ==== Shell ==== |
| 95 | |
| 96 | **nombresPremiers.submit** |
| 97 | |
| 98 | {{{ |
| 99 | universe = vanilla |
| 100 | |
| 101 | executable = nombresPremiers.sh |
| 102 | |
| 103 | # Arguments à passer à l'executable |
| 104 | arguments = 100 |
| 105 | |
| 106 | # Fichier de sortie à rappatrier depuis les nœuds d'execution |
| 107 | output=results.output.$(Process) |
| 108 | error=results.error.$(Process) |
| 109 | log=results.log |
| 110 | |
| 111 | should_transfer_files=YES |
| 112 | when_to_transfer_output = ON_EXIT |
| 113 | |
| 114 | queue |
| 115 | |
| 116 | }}} |
| 117 | |
| 118 | **nombresPremiers.sh** |
| 119 | |
| 120 | {{{ |
| 121 | #!/bin/sh |
| 122 | |
| 123 | limit=${1} |
| 124 | echo "Les nombres premiers entre 1 et ${limit} sont :" |
| 125 | |
| 126 | # Utilisation du crible d'Ératosthène pour trouver les nombres premiers jusqu'à "limit" |
| 127 | sieve=( $(seq 2 $limit) ) |
| 128 | count=0 |
| 129 | |
| 130 | for ((i=2; i*i<=limit; i++)) |
| 131 | do |
| 132 | if [ ${sieve[$i-2]} -ne 0 ] |
| 133 | then |
| 134 | for ((j=i*i; j<=limit; j+=i)) |
| 135 | do |
| 136 | sieve[$j-2]=0 |
| 137 | done |
| 138 | fi |
| 139 | done |
| 140 | |
| 141 | # Affichage des nombres premiers restants |
| 142 | for num in "${sieve[@]}" |
| 143 | do |
| 144 | if [ $num -ne 0 ] |
| 145 | then |
| 146 | echo -n "$num " |
| 147 | count=$((count + 1)) |
| 148 | |
| 149 | if [ $count -eq 50 ] |
| 150 | then |
| 151 | echo |
| 152 | count=0 |
| 153 | fi |
| 154 | fi |
| 155 | done |
| 156 | |
| 157 | echo # Saut de ligne final si nécessaire |
| 158 | |
| 159 | }}} |
| 160 | |
| 161 | ==== MPI avec OpenMPI ==== |
| 162 | |
| 163 | Dans l'univers vanilla, les tâches MPI ne tourne que sur une seule machine en utilisant tous les cœurs et processeurs demandés dans la limite des capacités de la machine hôte. |
| 164 | |
| 165 | **run.sh** |
| 166 | |
| 167 | run.sh est un script shell pour faire le setup de l'environnement. |
| 168 | |
| 169 | {{{ |
| 170 | #!/bin/bash |
| 171 | |
| 172 | export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib/:${LD_LIBRARY_PATH} |
| 173 | export PATH=/usr/lib64/openmpi/bin/:${PATH} |
| 174 | |
| 175 | mpirun $* |
| 176 | |
| 177 | }}} |
| 178 | |
| 179 | L'executable est compilé au préalable avec : {{{/usr/lib64/mpi/bin/mpicc simple.c -o simple}}} |
| 180 | |
| 181 | **simple.c** |
| 182 | |
| 183 | {{{ |
| 184 | #include <stdio.h> |
| 185 | #include <mpi.h> |
| 186 | |
| 187 | int main(int argc, char** argv) { |
| 188 | int rank, size; |
| 189 | |
| 190 | // Initialisation de l'environnement MPI |
| 191 | MPI_Init(&argc, &argv); |
| 192 | |
| 193 | // Obtention du rang du processus |
| 194 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
| 195 | |
| 196 | // Obtention de la taille du communicateur |
| 197 | MPI_Comm_size(MPI_COMM_WORLD, &size); |
| 198 | |
| 199 | // Affichage du rang et de la taille |
| 200 | printf("Hello from process %d of %d\n", rank, size); |
| 201 | |
| 202 | // Finalisation de l'environnement MPI |
| 203 | MPI_Finalize(); |
| 204 | |
| 205 | return 0; |
| 206 | } |
| 207 | |
| 208 | }}} |
| 209 | |
| 210 | **simple.submit** |
| 211 | {{{ |
| 212 | universe = vanilla |
| 213 | executable = run.sh |
| 214 | arguments = -np 8 simple |
| 215 | |
| 216 | output = simple.out |
| 217 | error = simple.err |
| 218 | log = simple.log |
| 219 | |
| 220 | should_transfer_files = yes |
| 221 | when_to_transfer_output = on_exit |
| 222 | transfer_input_files = simple |
| 223 | |
| 224 | # Spécifiez le nombre de slots (processus MPI) requis |
| 225 | request_cpus = 1 |
| 226 | request_memory = 1024M |
| 227 | request_disk = 10240K |
| 228 | |
| 229 | #+Requirements = OpSysAndVer =?= "AlmaLinux" |
| 230 | #+Requirements = machine =?= "lpsc-c21.in2p3.fr" |
| 231 | +Requirements = member(machine, {"lpsc-c22.in2p3.fr", "lpsc-c23.in2p3.fr"}) |
| 232 | |
| 233 | queue |
| 234 | |
| 235 | }}} |
| 236 | |
| 237 | ==== MPI avec MPICH ==== |
| 238 | |
| 239 | Dans l'univers vanilla, les tâches MPI ne tourne que sur une seule machine en utilisant tous les cœurs et processeurs demandés dans la limite des capacités de la machine hôte. |
| 240 | |
| 241 | **run.sh** |
| 242 | |
| 243 | run.sh est un script shell pour faire le setup de l'environnement. |
| 244 | |
| 245 | {{{ |
| 246 | #!/bin/bash |
| 247 | |
| 248 | export LD_LIBRARY_PATH=/usr/lib64/mpich/lib/:${LD_LIBRARY_PATH} |
| 249 | export PATH=/usr/lib64/mpich/bin/:${PATH} |
| 250 | |
| 251 | mpirun $* |
| 252 | |
| 253 | |
| 254 | }}} |
| 255 | |
| 256 | L'executable est compilé au préalable avec : {{{/usr/lib64/mpich/bin/mpicc simple.c -o simple}}} |
| 257 | |
| 258 | **simple.c** |
| 259 | |
| 260 | {{{ |
| 261 | #include <stdio.h> |
| 262 | #include <mpi.h> |
| 263 | |
| 264 | int main(int argc, char** argv) { |
| 265 | int rank, size; |
| 266 | |
| 267 | // Initialisation de l'environnement MPI |
| 268 | MPI_Init(&argc, &argv); |
| 269 | |
| 270 | // Obtention du rang du processus |
| 271 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
| 272 | |
| 273 | // Obtention de la taille du communicateur |
| 274 | MPI_Comm_size(MPI_COMM_WORLD, &size); |
| 275 | |
| 276 | // Affichage du rang et de la taille |
| 277 | printf("Hello from process %d of %d\n", rank, size); |
| 278 | |
| 279 | // Finalisation de l'environnement MPI |
| 280 | MPI_Finalize(); |
| 281 | |
| 282 | return 0; |
| 283 | } |
| 284 | }}} |
| 285 | |
| 286 | **simple.submit** |
| 287 | {{{ |
| 288 | universe = vanilla |
| 289 | executable = run.sh |
| 290 | |
| 291 | # Attention au chemin relatif pour l'executable avec MPICH: ./simple et pas simple |
| 292 | arguments = -np 8 ./simple |
| 293 | |
| 294 | output = simple.out |
| 295 | error = simple.err |
| 296 | log = simple.log |
| 297 | |
| 298 | should_transfer_files = yes |
| 299 | when_to_transfer_output = on_exit |
| 300 | transfer_input_files = simple |
| 301 | |
| 302 | # Spécifiez le nombre de slots (processus MPI) requis |
| 303 | request_cpus = 1 |
| 304 | request_memory = 1024M |
| 305 | request_disk = 10240K |
| 306 | |
| 307 | #+Requirements = OpSysAndVer =?= "AlmaLinux" |
| 308 | #+Requirements = machine =?= "lpsc-c21.in2p3.fr" |
| 309 | +Requirements = member(machine, {"lpsc-c22.in2p3.fr", "lpsc-c23.in2p3.fr"}) |
| 310 | |
| 311 | queue |
| 312 | |
| 313 | }}} |
| 314 | |
| 315 | |
| 316 | === Univers parallel === |
| 317 | |
| 318 | ==== Shell ==== |
| 319 | |
| 320 | **parallel.submit** |
| 321 | {{{ |
| 322 | #!/bin/sh |
| 323 | |
| 324 | echo "Hello I am ${HOSTNAME} and I am node ${1}" |
| 325 | |
| 326 | }}} |
| 327 | |
| 328 | **parallel.submit** |
| 329 | {{{ |
| 330 | universe = parallel |
| 331 | executable = parallel.sh |
| 332 | arguments = $(Node) |
| 333 | |
| 334 | machine_count = 2 |
| 335 | |
| 336 | output = parallel.$(Node).out |
| 337 | error = parallel.$(Node).err |
| 338 | # Il n'est pas possible de faire un fichier de log par nœuds |
| 339 | log = parallel.log |
| 340 | |
| 341 | should_transfer_files = yes |
| 342 | when_to_transfer_output = on_exit |
| 343 | |
| 344 | +Requirements = member(machine, {"lpsc-c21.in2p3.fr","lpsc-c22.in2p3.fr"}) |
| 345 | |
| 346 | queue |
| 347 | |
| 348 | }}} |
| 349 | |
| 350 | ==== MPI avec OpenMPI ==== |
| 351 | |
| 352 | Pour soumettre une tâche avec OpenMPI dans l'univers parallel, vous pouvez utiliser un script fourni par HTCondor et qui se trouve sur les machines de soumission sous : /usr/share/doc/condor/examples/openmpiscript. |
| 353 | |
| 354 | Dans l'exemple qui suit, on execute un fichier example préalablement compilé à partir d'un fichier source example.c : {{{/usr/lib64/openmpi/bin/mpicc example.c -o example}}} |
| 355 | |
| 356 | **example.c** |
| 357 | {{{ |
| 358 | #include <stdio.h> |
| 359 | #include <mpi.h> |
| 360 | |
| 361 | int main(int argc, char **argv) { |
| 362 | int rank, size; |
| 363 | int local_array[5] = {1, 2, 3, 4, 5}; |
| 364 | int global_sum[5] = {0, 0, 0, 0, 0}; |
| 365 | |
| 366 | MPI_Init(&argc, &argv); |
| 367 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
| 368 | MPI_Comm_size(MPI_COMM_WORLD, &size); |
| 369 | |
| 370 | // Chaque processus ajoute son tableau local à global_sum |
| 371 | |
| 372 | MPI_Reduce(local_array, global_sum, 5, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); |
| 373 | |
| 374 | // Seul le processus de rang 0 affiche le résultat |
| 375 | if (rank == 0) { |
| 376 | printf("Résultat de la somme globale : "); |
| 377 | for (int i = 0; i < 5; i++) { |
| 378 | printf("%d ", global_sum[i]); |
| 379 | } |
| 380 | printf("\n"); |
| 381 | } |
| 382 | |
| 383 | MPI_Finalize(); |
| 384 | return 0; |
| 385 | } |
| 386 | |
| 387 | }}} |
| 388 | |
| 389 | **example.submit** |
| 390 | {{{ |
| 391 | universe = parallel |
| 392 | |
| 393 | executable = /usr/share/doc/condor/examples/openmpiscript |
| 394 | arguments = example |
| 395 | |
| 396 | machine_count = 2 |
| 397 | |
| 398 | output = example.out |
| 399 | error = example.err |
| 400 | log = example.log |
| 401 | |
| 402 | should_transfer_files = yes |
| 403 | when_to_transfer_output = on_exit |
| 404 | transfer_input_files = example |
| 405 | want_io_proxy = True |
| 406 | |
| 407 | request_cpus = 8 |
| 408 | #request_memory = 1024M |
| 409 | #request_disk = 10240K |
| 410 | |
| 411 | #+Requirements = OpSysAndVer =?= "AlmaLinux" |
| 412 | +Requirements = member(machine, {"lpsc-c21.in2p3.fr","lpsc-c22.in2p3.fr"}) |
| 413 | |
| 414 | queue |
| 415 | |
| 416 | }}} |
| 417 | ==== MPI avec MPICH ==== |
| 418 | |
| 419 | Non disponible dans l'univers parallèle... |
| 420 | |
| 421 | === Univers java === |
| 422 | |
| 423 | Les programmes Java peuvent être exécutés après avoir été préalablement "compilés" avec un JDK (Java Devlopment Kit) normalement présent sur les machines de soumission: |
| 424 | * génération d'un fichier .class |
| 425 | {{{ |
| 426 | javac !MachineDate.java |
| 427 | }}} |
| 428 | * génération d'une archive exécutable .jar (optionnel) |
| 429 | {{{ |
| 430 | jar cf !MachineDate.jar ./!MachineDate.class |
| 431 | }}} |
| 432 | |
| 433 | **!MachineDate.java** |
| 434 | |
| 435 | {{{ |
| 436 | import java.net.InetAddress; |
| 437 | import java.util.Date; |
| 438 | |
| 439 | public class MachineDate { |
| 440 | public static void main(String[] args) { |
| 441 | try { |
| 442 | InetAddress localMachine = InetAddress.getLocalHost(); |
| 443 | System.out.println("Machine: " + localMachine.getHostName()); |
| 444 | System.out.println("Date: " + new Date()); |
| 445 | } catch (Exception e) { |
| 446 | e.printStackTrace(); |
| 447 | } |
| 448 | } |
| 449 | } |
| 450 | }}} |
| 451 | |
| 452 | |
| 453 | **Option 1 : !MachineDate.class** |
| 454 | |
| 455 | {{{ |
| 456 | universe = java |
| 457 | |
| 458 | executable = MachineDate.class |
| 459 | arguments = MachineDate |
| 460 | transfer_input_files = ${PWD}/MachineDate.class |
| 461 | |
| 462 | log = MachineDateClass.log |
| 463 | output = MachineDateClass.out |
| 464 | error = MachineDateClass.err |
| 465 | |
| 466 | should_transfer_files = YES |
| 467 | when_to_transfer_output = ON_EXIT |
| 468 | |
| 469 | request_cpus = 1 |
| 470 | request_memory = 1024M |
| 471 | request_disk = 10240K |
| 472 | |
| 473 | queue |
| 474 | |
| 475 | }}} |
| 476 | |
| 477 | **Option 2 : !MachineDate.jar** |
| 478 | |
| 479 | {{{ |
| 480 | universe = java |
| 481 | |
| 482 | executable = MachineDate.class |
| 483 | jar_files = MachineDate.jar |
| 484 | arguments = MachineDate |
| 485 | |
| 486 | log = MachineDateJar.log |
| 487 | output = MachineDateJar.out |
| 488 | error = MachineDateJar.err |
| 489 | |
| 490 | transfer_input_files = ${PWD}/MachineDate.jar |
| 491 | should_transfer_files = YES |
| 492 | when_to_transfer_output = ON_EXIT |
| 493 | |
| 494 | queue |
| 495 | }}} |
| 496 | |
| 497 | === Univers docker === |
| 498 | |
| 499 | Docker est déployé sur les machines de la ferme locale. Il est possible d'utiliser n'importe quelle image pour définir un environnement d’exécution pour vos tâches. |
| 500 | Les images docker peuvent être trouvées sur le site [https://hub.docker.com/ DockerHub] |
| 501 | |
| 502 | ==== Exécuter "cat" sur Debian ==== |
| 503 | |
| 504 | **docker.submit** |
| 505 | |
| 506 | {{{ |
| 507 | #universe = docker is optional |
| 508 | universe = docker |
| 509 | |
| 510 | # nom de l'image sur dockerHub |
| 511 | docker_image = debian |
| 512 | |
| 513 | # programme à exécuter dans l'environnement debian avec ses arguments |
| 514 | executable = /bin/cat |
| 515 | arguments = /etc/hosts |
| 516 | |
| 517 | # fichiers de sortie à rapatrier depuis le nœud d’exécution de la ferme locale |
| 518 | should_transfer_files = YES |
| 519 | when_to_transfer_output = ON_EXIT |
| 520 | output = out.$(Process) |
| 521 | error = err.$(Process) |
| 522 | log = log.$(Process) |
| 523 | |
| 524 | # pré-requis pour sélection d'un nœud de la ferme locale où faire tourner la tâche |
| 525 | request_cpus = 1 |
| 526 | request_memory = 1024M |
| 527 | request_disk = 10240K |
| 528 | |
| 529 | queue 1 |
| 530 | }}} |
| 531 | |
| 532 | ==== Génération d'un histogramme avec ROOT ==== |
| 533 | |
| 534 | ROOT est un programme d'analyse qui n'est pas forcément installé ou configuré sur les machines de la ferme locale. |
| 535 | Il est cependant possible de l'utiliser via une image docker. |
| 536 | |
| 537 | **Candle Histo.C** : le fichier root à exécuter |
| 538 | {{{ |
| 539 | #include <TROOT.h> |
| 540 | #include <TCanvas.h> |
| 541 | #include <TH2I.h> |
| 542 | #include <TRandom.h> |
| 543 | |
| 544 | void candlehisto() |
| 545 | { |
| 546 | // Définissez la taille du canevas avec une résolution plus élevée |
| 547 | TCanvas *c1 = new TCanvas("c1", "Candle Presets", 1920, 1080); |
| 548 | c1->Divide(3, 2); |
| 549 | |
| 550 | TRandom *rng = new TRandom(); |
| 551 | TH2I *h1 = new TH2I("h1", "Sin", 18, 0, 360, 100, -1.5, 1.5); |
| 552 | h1->GetXaxis()->SetTitle("Deg"); |
| 553 | |
| 554 | float myRand; |
| 555 | for (int i = 0; i < 360; i += 10) |
| 556 | { |
| 557 | for (int j = 0; j < 100; j++) |
| 558 | { |
| 559 | myRand = rng->Gaus(sin(i * 3.14 / 180), 0.2); |
| 560 | h1->Fill(i, myRand); |
| 561 | } |
| 562 | } |
| 563 | |
| 564 | c1->cd(1); |
| 565 | for (int i = 1; i < 7; i++) |
| 566 | { |
| 567 | c1->cd(i); |
| 568 | TString title = TString::Format("CANDLEX%d", i); |
| 569 | TH2I *myhist = (TH2I *)h1->DrawCopy(title); |
| 570 | myhist->SetTitle(title); |
| 571 | } |
| 572 | |
| 573 | // Sauvegardez dans un seul fichier image dans le répertoire courant |
| 574 | TString imgFileName = "output.png"; |
| 575 | c1->SaveAs(imgFileName); |
| 576 | } |
| 577 | |
| 578 | int main() |
| 579 | { |
| 580 | candlehisto(); |
| 581 | return 0; |
| 582 | } |
| 583 | |
| 584 | }}} |
| 585 | |
| 586 | **candlehisto.submit** : le fichier de soumission |
| 587 | |
| 588 | {{{ |
| 589 | #universe = docker is optional |
| 590 | universe = docker |
| 591 | |
| 592 | # nom de l'image sur dockerHub |
| 593 | docker_image = rootproject/root |
| 594 | |
| 595 | # programme à exécuter dans l'environnement ROOT avec ses arguments |
| 596 | executable = /opt/root/bin/root.exe |
| 597 | arguments = -b -q -l candlehisto.C |
| 598 | |
| 599 | # Fichier d'input à transférer sur le nœud où va tourner la tâche |
| 600 | transfer_input_files = candlehisto.C |
| 601 | |
| 602 | # Fichier de résultat à transférer depuis le nœud ou a tourner la tâche en fin d’exécution |
| 603 | transfer_output_files = output.png |
| 604 | |
| 605 | should_transfer_files = YES |
| 606 | when_to_transfer_output = ON_EXIT |
| 607 | |
| 608 | # Fichier de logs |
| 609 | output = out.$(ClusterId).$(ProcId) |
| 610 | error = err.$(ClusterId).$(ProcId) |
| 611 | log = log.$(ClusterId).$(ProcId) |
| 612 | |
| 613 | # Pré-requis pour la sélection du nœud local ou faire tourner la tâche |
| 614 | # mémoire requise |
| 615 | request_memory = 2000M |
| 616 | # type de système requis |
| 617 | +Requirements = OpSysAndVer =?= "AlmaLinux" |
| 618 | # nom du nœud d’exécution de la ferme locale |
| 619 | +Requirements = machine =?= "lpsc-c27.in2p3.fr" |
| 620 | |
| 621 | queue 1 |
| 622 | |
| 623 | }}} |
| 624 | |
| 625 | == Le concept de jobset == |
| 626 | |
| 627 | HTCondor permet la soumission d'ensemble de tâches. La syntaxe du fichier de soumission est légèrement différente que pour une tâche simple, vous pouvez trouver la documentation [https://htcondor.readthedocs.io/en/23.0/users-manual/job-sets.html#submitting-a-job-set ici] |
| 628 | |
| 629 | La commande HTcondor diffère également, elle est de la forme suivante : {{{htcondor jobset submit jobs.set}}} |
| 630 | |
| 631 | === Soumission d'un ensemble de tâches OpenMPI === |
| 632 | |
| 633 | On retrouve dans le fichier jobs.set le contenu d'un fichier de soumission de l'univers vanilla. La différence réside dans son "encapsulation" dans un itérateur dans le ficher de soumission du jobset. |
| 634 | |
| 635 | **jobs.set** |
| 636 | {{{ |
| 637 | name = ExampleJobSet |
| 638 | |
| 639 | # Définition d'un itérateur: un tableau contenant des items qui sont des liste d'arguments (dans notre cas, un seul argument par item, le nom d'une machine) |
| 640 | |
| 641 | iterator = table machine { |
| 642 | lpsc-c0.in2p3.fr |
| 643 | lpsc-c8.in2p3.fr |
| 644 | lpsc-c12.in2p3.fr |
| 645 | lpsc-c13.in2p3.fr |
| 646 | lpsc-c15.in2p3.fr |
| 647 | lpsc-c16.in2p3.fr |
| 648 | lpsc-c17.in2p3.fr |
| 649 | lpsc-c18.in2p3.fr |
| 650 | lpsc-c19.in2p3.fr |
| 651 | lpsc-c20.in2p3.fr |
| 652 | lpsc-c21.in2p3.fr |
| 653 | lpsc-c22.in2p3.fr |
| 654 | lpsc-c23.in2p3.fr |
| 655 | lpsc-c24.in2p3.fr |
| 656 | lpsc-c25.in2p3.fr |
| 657 | lpsc-c26.in2p3.fr |
| 658 | lpsc-c27.in2p3.fr |
| 659 | } |
| 660 | |
| 661 | # définition de la tâche de base qui va être "itérée" |
| 662 | |
| 663 | job { |
| 664 | |
| 665 | universe = vanilla |
| 666 | executable = run.sh |
| 667 | arguments = -np 8 example |
| 668 | |
| 669 | output = example_$(machine).out |
| 670 | error = example_$(machine).err |
| 671 | log = example.log |
| 672 | |
| 673 | should_transfer_files = yes |
| 674 | when_to_transfer_output = on_exit |
| 675 | transfer_input_files = example |
| 676 | |
| 677 | # renommage du fichier de sortie en fonction du nom de la machine |
| 678 | transfer_output_remaps = "example_$(machine).out=$(machine)/example_$(machine).out ; example_$(machine).err=$(machine)/example_$(machine).err" |
| 679 | |
| 680 | # Spécifiez le nombre de slots (processus MPI) requis |
| 681 | request_cpus = 1 |
| 682 | request_memory = 1024M |
| 683 | request_disk = 10240K |
| 684 | |
| 685 | +Requirements = machine =?= "$(machine)" |
| 686 | +WantParallelSchedulingGroups = True |
| 687 | |
| 688 | queue |
| 689 | } |
| 690 | |
| 691 | }}} |
| 692 | |
| 693 | |
| 694 | == Autre exemples == |
| 695 | {{{ |
| 696 | more testprog01.sh |
| 697 | #! /bin/sh |
| 698 | echo "HT-condor testprog01" |
| 699 | echo "I'm process id $$ on" `hostname` |
| 700 | echo "This is sent to standard error" 1>&2 |
| 701 | date |
| 702 | echo "Running as binary $0" "$@" |
| 703 | echo "My name (argument 1) is $1" |
| 704 | echo "My sleep duration (argument 2) is $2" |
| 705 | sleep $2 |
| 706 | echo "Sleep of $2 seconds finished. Exiting" |
| 707 | exit 0 |
| 708 | }}} |
| 709 | |
| 710 | ==== exemple d'un fichier de description de soumission: ==== |
| 711 | {{{ |
| 712 | more testprog01.submit |
| 713 | executable=testprog01.sh |
| 714 | universe=vanilla |
| 715 | arguments=Example.$(Cluster).$(Process) 100 |
| 716 | output=results.output.$(Process) |
| 717 | error=results.error.$(Process) |
| 718 | log=results.log |
| 719 | notification=never |
| 720 | should_transfer_files=YES |
| 721 | when_to_transfer_output = ON_EXIT |
| 722 | queue |
| 723 | }}} |
| 724 | |
| 725 | ==== exemple d'un fichier de description de soumission en sélectionnant !AlmaLinux9 comme Operating System:==== |
| 726 | {{{ |
| 727 | more testprog2.submit |
| 728 | executable=testprog01.sh |
| 729 | universe=vanilla |
| 730 | arguments=Example.$(Cluster).$(Process) 100 |
| 731 | output=results.output.$(Process) |
| 732 | error=results.error.$(Process) |
| 733 | log=results.log |
| 734 | notification=never |
| 735 | should_transfer_files=YES |
| 736 | when_to_transfer_output = ON_EXIT |
| 737 | requirements = (OpSysAndVer =?= "AlmaLinux9") |
| 738 | queue |
| 739 | }}} |
| 740 | |
| 741 | Pour choisir Centos7: |
| 742 | {{{ |
| 743 | requirements = (OpSysAndVer =?= "CentOS7") |
| 744 | }}} |
| 745 | Pour choisir Fedora39: |
| 746 | {{{ |
| 747 | requirements = (OpSysAndVer =?= "Fedora39") |
| 748 | }}} |
| 749 | L'utilisateur définit lui-même le groupe auxquel il appartient via une instruction de type : |
| 750 | {{{ |
| 751 | accounting_group = informatique |
| 752 | }}} |