{"id":1456,"date":"2019-09-30T14:08:55","date_gmt":"2019-09-30T21:08:55","guid":{"rendered":"https:\/\/in.nau.edu\/hpc\/?page_id=1456"},"modified":"2024-08-14T09:25:04","modified_gmt":"2024-08-14T16:25:04","slug":"job-arrays","status":"publish","type":"page","link":"https:\/\/in.nau.edu\/arc\/overview\/using-the-cluster-advanced\/job-arrays\/","title":{"rendered":"Job Arrays"},"content":{"rendered":"<h1>Job Arrays<\/h1>\n<p>Job arrays allow you to leverage SLURM\u2019s ability to create multiple jobs from one script. Many of the situations where this is useful include:<\/p>\n<ul>\n<li>Establishing a list of commands to run and have a job created from each command in the list.<\/li>\n<li>Running many parameters against one set of data or analysis program.<\/li>\n<li>Running the same program multiple times with different sets of data.<\/li>\n<\/ul>\n<p>In these cases, as we have learned thus far, we would have to manually rerun the sbatch command multiple times for each of the aforementioned scenarios. Fortunately, SLURM allows us to automate this procedure using job arrays. Each array is considered to be one \u201carray job\u201d that has a specific ID. Each element of the array is one array task, which has it\u2019s own sub-ID. For example, if your \u201carray job\u201d ID was <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">1212985<\/span>, your first \u201carray task\u201d that runs would have an ID of <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">1212985_0<\/span>.<\/p>\n<p><strong>NOTE:<\/strong><\/p>\n<p>Take note that if you use these examples that you will need to replace <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">NAUID<\/span> with your own id, and that you will need to take out the line numbers. Also, we use a directory called playground and you will need to create one as well or it will not run.<\/p>\n<h2>Examples<\/h2>\n<h3>Iteratively named input\/reference files<\/h3>\n<p>If your input files are numerically sequenced, each individual (sub-)job in the array read a single, unique input file: one array (sub-)job for each input file. (E.g.: a job-array set to range from 1-4 would utilize one file, from file-1 through file-4, over its total of 4 sub-jobs.)<\/p>\n<p>To set-up this type of job-array, a researcher could set the start-index\/end-index of the job-array to match the iterating part of the input files; or they could sequentially rename existing input files. Then, by using <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">$SLURM_ARRAY_TASK_ID<\/span> as part of a filename, each sub-job could operate on its own separate input file. For the 4-files example:<br \/>\n<code>$ ls -1 ~\/data<br \/>\nsample-1.dat<br \/>\nsample-2.dat<br \/>\nsample-3.dat<br \/>\nsample-4.dat<\/code><\/p>\n<p>The job-script for the overall array-job would then just run whatever number-crunching tasks were necessary on a derived filename:<\/p>\n<pre><code>#SBATCH --array=1-4\r\n\r\ntaskfile=\"~\/data\/sample-$SLURM_ARRAY_TASK_ID.dat\"\r\nsrun analyze_datafile \"$taskfile\"\r\nsrun process_datafile \"$taskfile\"\r\n<\/code><\/pre>\n<h3>Irregularly named input\/reference files<\/h3>\n<p>If necessary input files aren&#8217;t sequentially named, it&#8217;s still quite easy to set-up a job-array to iterate through the entire set, with just a bit of basic shell-scripting.<\/p>\n<div style=\"background-color: beige; border: 1px solid brown; padding: 12px;\">\n<p><b>Helpful BASH scripting tips:<\/b><\/p>\n<ul>\n<li>To save the output of a command to a BASH variable, use this <i>command substitution<\/i> construction:<br \/>\n<span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white;\">variable_name=$()<\/span><\/li>\n<li>To use one command&#8217;s output as the input of a second command, use this <i>pipe redirect<\/i> construction:<br \/>\n<span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white;\">command_1 | command_2<\/span><\/li>\n<li>To &#8220;capture&#8221; the N&#8217;th line of input using <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white;\">awk<\/span>, use awk&#8217;s number-of-row selector:<br \/>\n<span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white;\">&lt;piped_input&gt; | awk &#8220;NR==#&#8221;<\/span><br \/>\n<i>or<\/i><br \/>\n<span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white;\">awk &#8220;NR==#&#8221; &lt;input_file&gt;<\/span><\/li>\n<\/ul>\n<\/div>\n<p><code>$ ls -1 ~\/data\/<br \/>\nsample_2021_01.dat<br \/>\nsample_2021_07.dat<br \/>\nsample_2022_01.dat<br \/>\nsample_2022_07.dat<br \/>\n$ filelist=$( ls -1 ~\/data\/ )<br \/>\n$ echo \"$filelist\" | awk \"NR==1\"<br \/>\nsample_2021_01.dat<br \/>\n$ echo \"$filelist\" | awk \"NR==2\"<br \/>\nsample_2021_07.dat<br \/>\n$ echo \"$filelist\" | awk \"NR==3\"<br \/>\nsample_2022_01.dat<br \/>\n$ echo \"$filelist\" | awk \"NR==4\"<br \/>\nsample_2022_07.dat<\/code><\/p>\n<p>Again, the job-script for the overall array-job would then just run whatever number-crunching tasks were necessary on a derived filename:<\/p>\n<pre><code>#SBATCH --array=1-4\r\n\r\nfilelist=$( ls -1 ~\/data\/ )\r\ntaskfile=$( echo \"$filelist\" | awk \"NR==$SLURM_ARRAY_TASK_ID\" )\r\nsrun analyze_datafile \"$taskfile\"\r\nsrun process_datafile \"$taskfile\"<\/code><\/pre>\n<h3>Single input source with varying parameters<\/h3>\n<p><code>$ cat ~\/coords.txt<br \/>\n35.19N,111.6W<br \/>\n33.57N,112.1W<br \/>\n32.15N,110.9W<br \/>\n$ cat ~\/coords.txt | awk \"NR==1\"<br \/>\n35.19N,111.6W<br \/>\n$ cat ~\/coords.txt | awk \"NR==2\"<br \/>\n33.57N,112.1W<br \/>\n$ cat ~\/coords.txt | awk \"NR==3\"<br \/>\n32.15N,110.9W<br \/>\n$ taskcoord=$(cat ~\/coords.txt | awk \"NR==$SLURM_ARRAY_TASK_ID\")<\/code><\/p>\n<h2>Old Examples<\/h2>\n<!-- shortcode-accordion -->\n<div class=\"shortcode-accordion shortcode-accordion--closed\" style=\"position: relative;\" >\n        <a class=\"shortcode-accordion__trigger\" data-header=\"Exercise 1: Numbered Files_0\" href=\"#\">\n      <div class=\"shortcode-accordion__header\">\n          <h4>Exercise 1: Numbered Files <span class=\"screen-reader-text\">Accordion Closed<\/span><\/h4>\n          <span class=\"shortcode-accordion__header__arrow\"><\/span>\n      <\/div>\n    <\/a>\n    <div class=\"shortcode-accordion__body\">\n        <!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body>\n<p>There may be times that you would like to send many files as input to a program. Instead of having to do this one at a time, you can set up a job array to do this automatically. In this next example, we will be using a shell script called <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">exercise1.sh<\/span> that takes input files that are numbered and puts the data into an output file.<\/p>\n<pre><strong>Exercise1.sh<\/strong>\r\n<code>#!\/bin\/bash\r\n\r\n#SBATCH --job-name=exercise1\r\n#SBATCH --time=00:01:00\r\n#SBATCH --mem=1\r\n#SBATCH --input=\/scratch\/NAUID\/playground\/files\/input_%a.csv\r\n#SBATCH --output=\/scratch\/NAUID\/playground\/output_%a.txt\r\n#SBATCH --chdir=\/scratch\/NAUID\/playground\/files\r\n#SBATCH --array=1-3\r\n\r\nfilename=\"input_${SLURM_ARRAY_TASK_ID}.csv\"\r\nsrun echo \"File: $filename\"\r\nsrun cat $filename<\/code><\/pre>\n<p>In this example there are two lines that we use which are very important to be able to access the files. In line 6 we specify the input file. Since our files are labeled in a formatting of <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">input_x.csv<\/span>, then we can specify our input files <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">using input_%a.csv<\/span>. The <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">%a<\/span> specification denotes the job array ID (index) number. In line 12 we use a variable called <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">SLURM_ARRAY_TASK_ID<\/span>. This variable is similar to <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">%a<\/span> and is used to denote the usage of the job array ID (index) number.<\/p>\n<\/body><\/html>\n\n    <\/div>\n<\/div>\n\n<!-- shortcode-accordion -->\n<div class=\"shortcode-accordion shortcode-accordion--closed\" style=\"position: relative;\" >\n        <a class=\"shortcode-accordion__trigger\" data-header=\"Exercise 2: Non-numbered Files_0\" href=\"#\">\n      <div class=\"shortcode-accordion__header\">\n          <h4>Exercise 2: Non-numbered Files <span class=\"screen-reader-text\">Accordion Closed<\/span><\/h4>\n          <span class=\"shortcode-accordion__header__arrow\"><\/span>\n      <\/div>\n    <\/a>\n    <div class=\"shortcode-accordion__body\">\n        <!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body>\n<p>Similar to example 1, in this example we will be taking in different input files from a specified directory. Instead of reading the files that are labeled with sequential numbers, we will be reading files that are labeled nonnumerical titles using a file list. In this next example, we will be using a shell script called <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">exercise2.sh<\/span> that takes input files from a directory that are nonnumerical and puts the data into an output file.<\/p>\n<pre><strong>File_list.txt<\/strong>\r\n<code>avondale.csv\r\nflagstaff.csv\r\ngilbert.csv\r\ngoodyear.csv\r\nphoenix.csv\r\nwilliams.csv<\/code> <strong>Exercise2.sh<\/strong> #!\/bin\/bash #SBATCH --job-name=exercise2 #SBATCH --time=00:01:00 #SBATCH --mem=1 #SBATCH --output=\/scratch\/NAUID\/playground\/output_%a #SBATCH --chdir=\/scratch\/NAUID\/playground\/ #SBATCH --array=1-6 file=$(awk \"NR==${SLURM_ARRAY_TASK_ID}\" file_list.txt) srun echo \"Data from $file: \" srun cat .\/cityfiles\/$file<\/pre>\n<p>In this example there is one line that makes this different from example 1. In line 10 we are using the <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">awk<\/span> command. We are using <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">awk<\/span> to to read the file line by line, where <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">NR<\/span> is equivalent to the job array ID (index) number and <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">NR<\/span> is equivalent to the number of records seen so far (line number). Line 12 is then using the file name that we got from line 10 and writing the data to our output file.<\/p>\n<\/body><\/html>\n\n    <\/div>\n<\/div>\n\n<!-- shortcode-accordion -->\n<div class=\"shortcode-accordion shortcode-accordion--closed\" style=\"position: relative;\" >\n        <a class=\"shortcode-accordion__trigger\" data-header=\"Exercise 3: Running Multiple Programs Against Data_0\" href=\"#\">\n      <div class=\"shortcode-accordion__header\">\n          <h4>Exercise 3: Running Multiple Programs Against Data <span class=\"screen-reader-text\">Accordion Closed<\/span><\/h4>\n          <span class=\"shortcode-accordion__header__arrow\"><\/span>\n      <\/div>\n    <\/a>\n    <div class=\"shortcode-accordion__body\">\n        <!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body>\n<p>Taking what we learned in example 2, we can take the input files and run them against multiple programs using a command list. This can be used in the chance that you need to take your data files&nbsp; and run them against multiple programs. In the example below, we are taking our input files and running them against the following scripts: add_numbers, multiply_numbers, and subtract_numbers. In the programs we are using we pass through three numbers and compute calculations.<\/p>\n<pre><strong>command_list<\/strong>\r\n<code>add_numbers\r\nmultiply_numbers\r\nsubtract_numbers\r\n<\/code> <strong>add_numbers<\/strong> <code>#!\/bin\/env python3\r\n\r\nimport sys\r\n\r\ndata=sys.argv[1]\r\ndata=data.split(\",\")\r\nnum1=data[0]\r\nnum2=data[1]\r\nnum3=data[2]\r\ntotal=int(num1)+int(num2)+int(num3)\r\nprint(total)\r\n<\/code> <strong>multiply_numbers<\/strong> <code>#!\/bin\/env python3\r\n\r\nimport sys\r\ndata=sys.argv[1]\r\ndata=data.split(\",\")\r\nnum1=data[0]\r\nnum2=data[1]\r\nnum3=data[2]\r\ntotal=int(num1)*int(num2)*int(num3)\r\nprint(total)\r\n<\/code> <b>subtract_numbers<\/b> <code>#!\/bin\/env python3\r\n\r\nimport sys\r\n\r\ndata=sys.argv[1]\r\ndata=data.split(\",\")\r\nnum1=data[0]\r\nnum2=data[1]\r\nnum3=data[2]\r\ntotal=int(num1)-int(num2)-int(num3)\r\nprint(total)\r\n<\/code> <strong>Exercise3.sh<\/strong> <code>#!\/bin\/bash\r\n\r\n#SBATCH --job-name=exercise3\r\n#SBATCH --time=00:01:00\r\n#SBATCH --mem=1\r\n#SBATCH --output=\/scratch\/NAUID\/playground\/output_%a\r\n#SBATCH --chdir=\/scratch\/NAUID\/playground\r\n#SBATCH --array=1-6\r\n\r\nmodule load python\/3.latest\r\nfile=$(awk \"NR==${SLURM_ARRAY_TASK_ID}\" file_list.txt)  \r\nsrun echo \"File: $file\"\r\nfor line in `cat command_list`; do\r\n    echo \"Script: $line\"\r\n    for data in `cat .\/cityfiles\/$file`; do\r\n        srun python $line $data\r\n    done\r\ndone<\/code><\/pre>\n<p>In the example above, similar to example 2 line 12 is obtaining the file names from the <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">file_list<\/span>. From there, starting at line 17 we have started a loop that gets each line of the <span style=\"font-size: 16px; font-family: monospace; border: 1px solid; border-radius: 4px; padding: 0px 4px 0px; border-color: #BBBBBB; background-color: white\">command_list<\/span> and grabs the program to run. Then line 20 grabs each line of data in our input files and passes it to the program as seen in line 21. Once the job runs you will see output files like the one seen below.<\/p>\n<pre><b>Output_1<\/b>\r\n<code>File: avondale.csv\r\nScript: add_numbers\r\n369\r\n639\r\n1893\r\nScript: multiply_numbers\r\n1860867\r\n9663597\r\n251239591\r\nScript: subtract_numbers\r\n-123\r\n-213\r\n-631<\/code><\/pre>\n<\/body><\/html>\n\n    <\/div>\n<\/div>\n\n<!-- shortcode-accordion -->\n<div class=\"shortcode-accordion shortcode-accordion--closed\" style=\"position: relative;\" >\n        <a class=\"shortcode-accordion__trigger\" data-header=\"Referenced Files_0\" href=\"#\">\n      <div class=\"shortcode-accordion__header\">\n          <h4>Referenced Files <span class=\"screen-reader-text\">Accordion Closed<\/span><\/h4>\n          <span class=\"shortcode-accordion__header__arrow\"><\/span>\n      <\/div>\n    <\/a>\n    <div class=\"shortcode-accordion__body\">\n        <!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body>\n<pre><strong>Input_1.csv<\/strong>\r\n<code>x1,x2,x3\r\n123,345,12\r\n213,230,127\r\n543,345,209\r\n631,183,324\r\n345,789,901<\/code>\r\n<strong>Avondale.csv<\/strong>\r\n<code>123,345,12\r\n213,230,127\r\n631,183,324<\/code>\r\n<\/pre>\n<\/body><\/html>\n\n    <\/div>\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Job Arrays Job arrays allow you to leverage SLURM\u2019s ability to create multiple jobs from one script. Many of the situations where this is useful include: Establishing a list of commands to run and have a job created from each command in the list. Running many parameters against one set of data or analysis program. [&hellip;]<\/p>\n","protected":false},"author":469,"featured_media":145,"parent":71,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_relevanssi_hide_post":"","_relevanssi_hide_content":"","_relevanssi_pin_for_all":"","_relevanssi_pin_keywords":"","_relevanssi_unpin_keywords":"","_relevanssi_related_keywords":"","_relevanssi_related_include_ids":"","_relevanssi_related_exclude_ids":"","_relevanssi_related_no_append":"","_relevanssi_related_not_related":"","_relevanssi_related_posts":"","_relevanssi_noindex_reason":"","ring_central_script_selection":"","footnotes":""},"class_list":["post-1456","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/pages\/1456","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/users\/469"}],"replies":[{"embeddable":true,"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/comments?post=1456"}],"version-history":[{"count":36,"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/pages\/1456\/revisions"}],"predecessor-version":[{"id":3608,"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/pages\/1456\/revisions\/3608"}],"up":[{"embeddable":true,"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/pages\/71"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/media\/145"}],"wp:attachment":[{"href":"https:\/\/in.nau.edu\/arc\/wp-json\/wp\/v2\/media?parent=1456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}