Back to home page

EIC code displayed by LXR

 
 

    


Warning, /tutorial-developing-benchmarks/episodes/01-analysis_scripts_and_snakemake.md is written in an unsupported language. File is not indexed.

0001 ---
0002 title: "Exercise 1: Analysis Scripts and Snakemake"
0003 teaching: 20
0004 exercises: 10
0005 ---
0006 
0007 ::::::::::::::::::::::::::::::::::::::::::::::: questions
0008 
0009 - How does one set up data analysis workflows?
0010 
0011 :::::::::::::::::::::::::::::::::::::::::::::::
0012 
0013 ::::::::::::::::::::::::::::::::::::::::::::::: objectives
0014 
0015 - Learn basics of creating Snakemake workflows
0016 
0017 :::::::::::::::::::::::::::::::::::::::::::::::
0018 
0019 In this exercise we start with a ready-made analysis script that we're running locally. We'll practice using [Snakemake](https://snakemake.github.io/) workflow management system to define data analysis pipelines.
0020 
0021 Snakemake comes with a great [documentation](https://snakemake.readthedocs.io), you are encouraged to read it. For now, let's cover its suggested use for needs of defining ePIC benchmarks.
0022 
0023 ## Starting from an Analysis Script
0024 
0025 We're going to go all the way from an analysis script to a fully-integragrated benchmark with GitLab's continuous integration (CI).
0026 
0027 First launch eic-shell
0028 
0029 ```bash
0030 ./eic-shell
0031 ```
0032 
0033 then create a working directory
0034 
0035 ```bash
0036 mkdir tutorial_directory
0037 mkdir tutorial_directory/starting_script
0038 cd tutorial_directory/starting_script
0039 ```
0040 
0041 Copy the following files to this working directory:
0042 
0043 - this analysis script: [`uchannelrho.cxx`](files/uchannelrho.cxx)
0044 - this plotting macro: [`plot_rho_physics_benchmark.C`](files/prefinal/plot_rho_physics_benchmark.C)
0045 - this style header: [`RiceStyle.h`](files/RiceStyle.h)
0046 
0047 We will also start by running over a file from the simulation campaign. Download it to your workspace:
0048 
0049 ```bash
0050 xrdcp root://dtn-eic.jlab.org//volatile/eic/EPIC/RECO/25.10.2/epic_craterlake/EXCLUSIVE/UCHANNEL_RHO/10x100/rho_10x100_uChannel_Q2of0to10_hiDiv.0020.eicrecon.edm4eic.root ./
0051 ```
0052 
0053 Organize files into `analysis` and `macros` directories:
0054 
0055 ```bash
0056 mkdir analysis
0057 mv uchannelrho.cxx analysis/
0058 mkdir macros
0059 mv plot_rho_physics_benchmark.C macros/
0060 mv RiceStyle.h macros/
0061 ```
0062 
0063 Run the analysis script over the simulation campaign output:
0064 
0065 ```bash
0066 root -l -b -q 'analysis/uchannelrho.cxx+("rho_10x100_uChannel_Q2of0to10_hiDiv.0020.eicrecon.edm4eic.root","output.root")'
0067 ```
0068 
0069 Now make a directory to contain the benchmark figures, and run the plotting macro:
0070 
0071 ```bash
0072 mkdir output_figures/
0073 root -l -b -q 'macros/plot_rho_physics_benchmark.C("output.root")'
0074 ```
0075 
0076 You should see some errors like
0077 
0078 ```output
0079 Error in <TTF::SetTextSize>: error in FT_Set_Char_Size
0080 ```
0081 
0082 but the figures should be produced just fine. If everything's run correctly, then we have a working analysis!
0083 
0084 With this analysis as a starting point, we'll next explore using Snakemake to define an analysis workflow.
0085 
0086 ## Getting started with Snakemake
0087 
0088 We'll now use a tool called Snakemake to define an analysis workflow that will come in handy when building analysis pipelines.
0089 
0090 In order to demonstrate the advantages of using snakefiles, let's start using them for our analysis.
0091 First let's use snakemake to grab some simulation campaign files from the online storage space. In your `tutorial_directory/starting_script/` directory make a new file called `Snakefile`.
0092 Open the file and add these lines:
0093 
0094 ```python
0095 rule your_benchmark_campaign_reco_get:
0096     output:
0097         f"sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.{{INDEX}}.eicrecon.edm4eic.root",
0098     shell: """
0099 xrdcp root://dtn-eic.jlab.org//volatile/eic/EPIC/RECO/25.10.2/epic_craterlake/EXCLUSIVE/UCHANNEL_RHO/10x100/rho_10x100_uChannel_Q2of0to10_hiDiv.{wildcards.INDEX}.eicrecon.edm4eic.root {output}
0100 """
0101 ```
0102 
0103 If you're having trouble copying and pasting, you can also copy from [the ready-made Snakefile](files/Snakefile).
0104 
0105 We also defined a new rule: `your_benchmark_campaign_reco_get`. This rule defines how to download a single file from the JLab servers to the location `sim_output`.
0106 
0107 After saving the Snakefile, let's try running it.
0108 
0109 The important thing to remember about Snakemake is that Snakemake commands behave like requests. So if I want Snakemake to produce a file called `output.root`, I would type `snakemake --cores 2 output.root`. If there is a rule for producing `output.root`, then Snakemake will find that rule and execute it. We've defined a rule to produce a file called `../../sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.{INDEX}.eicrecon.edm4eic.root`, but really we can see from the construction of our rule that the `{INDEX}` is a wildcard, so we should put a number there instead. Checking out the [files on S3](https://dtn01.sdcc.bnl.gov:9001/buckets/eictest/browse/RVBJQy9SRUNPLzI1LjEwLjIvZXBpY19jcmF0ZXJsYWtlL0VYQ0xVU0lWRS9VQ0hBTk5FTF9SSE8vMTB4MTAwLw==), we see files with indices from `0000` up to `0116`. Let's request that Snakemake download the file `rho_10x100_uChannel_Q2of0to10_hiDiv.0005.eicrecon.edm4eic.root`:
0110 
0111 ```bash
0112 snakemake --cores 2 sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.0000.eicrecon.edm4eic.root
0113 ```
0114 
0115 Snakemake now looks for the rule it needs to produce that file. It finds the rule we wrote, and it downloads the file. Check for the file:
0116 
0117 ```bash
0118 ls sim_output/
0119     rho_10x100_uChannel_Q2of0to10_hiDiv.0000.eicrecon.edm4eic.root
0120 ```
0121 
0122 Okay whatever... so we download a file. It doesn't look like Snakemake really adds anything at this point.
0123 
0124 But the benefits from using Snakemake become more apparent as the number of tasks we want to do grows! Let's now add a new rule below the last one:
0125 
0126 ```python
0127 rule your_benchmark_analysis:
0128     input:
0129         script=workflow.source_path("analysis/uchannelrho.cxx"),
0130         data=f"sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.{{INDEX}}.eicrecon.edm4eic.root",
0131     output:
0132         plots=f"sim_output/campaign_25.10.2_{{INDEX}}.eicrecon.edm4eic/plots.root",
0133     shell:
0134         """
0135 mkdir -p $(dirname "{output.plots}")
0136 root -l -b -q '{input.script}+("{input.data}","{output.plots}")'
0137 """
0138 ```
0139 
0140 This rule runs an analysis script to create ROOT files containing plots. The rule uses the simulation campaign file downloaded from JLab as input data, and it runs the analysis script `uchannelrho.cxx`. Note that we use `workflow.source_path()` to reference the script - this function returns the correct path to the script relative to the Snakefile's location in the benchmark directory.
0141 
0142 Now let's request the output file `"sim_output/campaign_25.10.2_0005.eicrecon.edm4eic/plots.root"`. When we request this, Snakemake will identify that it needs to run the new `your_benchmark_analysis` rule. But in order to do this, it now needs a file we don't have: `sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.0005.eicrecon.edm4eic.root` because we only downloaded the file with index `0000` already. What Snakemake will do automatically is recognize that in order to get that file, it first needs to run the `your_benchmark_campaign_reco_get` rule. It will do this first, and then circle back to the `your_benchmark_analysis` rule.
0143 
0144 Let's try it out:
0145 
0146 ```bash
0147 snakemake --cores 2 sim_output/campaign_25.10.2_0005.eicrecon.edm4eic/plots.root
0148 ```
0149 
0150 You should see something like this:
0151 ![Snakemake output second rule](fig/snakemake_output_rule2_new.png)
0152 Check for the output file:
0153 
0154 ```bash
0155 ls sim_output/campaign_25.10.2_0005.eicrecon.edm4eic/
0156 ```
0157 
0158 You should see `plots.root`.
0159 
0160 That's still not very impressive. Snakemake gets more useful when we want to run the analysis code over a lot of files. Let's add a rule to do this:
0161 
0162 ```python
0163 rule your_benchmark_combine:
0164     input:
0165         lambda wildcards: expand(
0166            f"sim_output/campaign_25.10.2_{{INDEX:04d}}.eicrecon.edm4eic/plots.root",
0167            INDEX=range(int(wildcards.N)),
0168         ),
0169     wildcard_constraints:
0170         N="\d+",
0171     output:
0172         f"sim_output/campaign_25.10.2_combined_{{N}}files.eicrecon.edm4eic.plots.root",
0173     shell:
0174         """
0175 hadd {output} {input}
0176 """
0177 ```
0178 
0179 On its face, this rule just adds root files using the `hadd` command. But by specifying the number of files you want to add, Snakemake will realize those files don't exist, and will go back to the `your_benchmark_campaign_reco_get` rule and the `your_benchmark_analysis` rule to create them.
0180 
0181 Let's test it out by requesting it combine 10 files:
0182 
0183 ```bash
0184 snakemake --cores 2 sim_output/campaign_25.10.2_combined_10files.eicrecon.edm4eic.plots.root
0185 ```
0186 
0187 It will spend some time downloading files and running the analysis code. Then it should hadd the files:
0188 ![Snakemake output third rule](fig/snakemake_output_rule3_new.png)
0189 
0190 Once it's done running, check that the file was produced:
0191 
0192 ```bash
0193 ls sim_output/campaign_25.10.2_combined_10files*
0194 ```
0195 
0196 Now let's add one more rule to create benchmark plots:
0197 
0198 ```python
0199 rule your_benchmark_plots:
0200     input:
0201         script=workflow.source_path("macros/plot_rho_physics_benchmark.C"),
0202         plots=f"sim_output/campaign_25.10.2_combined_{{N}}files.eicrecon.edm4eic.plots.root",
0203     output:
0204         f"sim_output/campaign_25.10.2_combined_{{N}}files.eicrecon.edm4eic.plots_figures/benchmark_rho_mass.pdf",
0205     shell:
0206         """
0207 if [ ! -d "{input.plots}_figures" ]; then
0208     mkdir "{input.plots}_figures"
0209     echo "{input.plots}_figures directory created successfully."
0210 else
0211     echo "{input.plots}_figures directory already exists."
0212 fi
0213 root -l -b -q '{input.script}("{input.plots}")'
0214 """
0215 ```
0216 
0217 Now run the new rule by requesting a benchmark figure made from 10 simulation campaign files:
0218 
0219 ```bash
0220 snakemake --cores 2 sim_output/campaign_25.10.2_combined_10files.eicrecon.edm4eic.plots_figures/benchmark_rho_mass.pdf
0221 ```
0222 
0223 Now check that the three benchmark figures were created:
0224 
0225 ```bash
0226 ls sim_output/campaign_25.10.2_combined_10files.eicrecon.edm4eic.plots_figures/*.pdf
0227 ```
0228 
0229 You should see three pdfs.
0230 We did it!
0231 
0232 Now that our Snakefile is totally set up, the big advantage of Snakemake is how it manages your workflow.
0233 If you edit the plotting macro and then rerun:
0234 
0235 ```bash
0236 snakemake --cores 2 sim_output/campaign_25.10.2_combined_10files.eicrecon.edm4eic.plots_figures/benchmark_rho_mass.pdf
0237 ```
0238 
0239 Snakemake will recognize that simulation campaign files have already been downloaded, that the analysis scripts have already run, and the files have already been combined. It will only run the last step, the plotting macro, if that's the only thing that needs to be re-run.
0240 
0241 If the analysis script changes, Snakemake will only re-run the analysis script and everything after.
0242 
0243 If we want to scale up the plots to include 15 simulation campaign files instead of just 10, then for those 5 extra files only Snakemake will rerun all the steps, and combine with the existing 10 files.
0244 
0245 The final Snakefile should look like this:
0246 
0247 ```python
0248 rule your_benchmark_campaign_reco_get:
0249     output:
0250         f"sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.{{INDEX}}.eicrecon.edm4eic.root",
0251     retries: 3
0252     shell: """
0253 xrdcp root://dtn-eic.jlab.org//volatile/eic/EPIC/RECO/25.10.2/epic_craterlake/EXCLUSIVE/UCHANNEL_RHO/10x100/rho_10x100_uChannel_Q2of0to10_hiDiv.{wildcards.INDEX}.eicrecon.edm4eic.root {output}
0254 """
0255 
0256 rule your_benchmark_analysis:
0257     input:
0258         script=workflow.source_path("analysis/uchannelrho.cxx"),
0259         data=f"sim_output/rho_10x100_uChannel_Q2of0to10_hiDiv.{{INDEX}}.eicrecon.edm4eic.root",
0260     output:
0261         plots=f"sim_output/campaign_25.10.2_{{INDEX}}.eicrecon.edm4eic/plots.root",
0262     shell:
0263         """
0264 mkdir -p $(dirname "{output.plots}")
0265 root -l -b -q '{input.script}+("{input.data}","{output.plots}")'
0266 """
0267 
0268 rule your_benchmark_combine:
0269     input:
0270         lambda wildcards: expand(
0271            f"sim_output/campaign_25.10.2_{{INDEX:04d}}.eicrecon.edm4eic/plots.root",
0272            INDEX=range(int(wildcards.N)),
0273         ),
0274     wildcard_constraints:
0275         N="\d+",
0276     output:
0277         f"sim_output/campaign_25.10.2_combined_{{N}}files.eicrecon.edm4eic.plots.root",
0278     shell:
0279         """
0280 hadd {output} {input}
0281 """
0282 
0283 rule your_benchmark_plots:
0284     input:
0285         script=workflow.source_path("macros/plot_rho_physics_benchmark.C"),
0286         plots=f"sim_output/campaign_25.10.2_combined_{{N}}files.eicrecon.edm4eic.plots.root",
0287     output:
0288         f"sim_output/campaign_25.10.2_combined_{{N}}files.eicrecon.edm4eic.plots_figures/benchmark_rho_mass.pdf",
0289     shell:
0290         """
0291 if [ ! -d "{input.plots}_figures" ]; then
0292     mkdir "{input.plots}_figures"
0293     echo "{input.plots}_figures directory created successfully."
0294 else
0295     echo "{input.plots}_figures directory already exists."
0296 fi
0297 root -l -b -q '{input.script}("{input.plots}")'
0298 """
0299 
0300 ```
0301 
0302 ## Conclusion
0303 
0304 In this exercise we've built an analysis workflow using Snakemake. That required us to think about the flow of the data and come up with a file naming scheme to reflect it. This approach can be scaled between local testing with handful of files and largely parallel analyses on full datasets.
0305 
0306 ::::::::::::::::::::::::::::::::::::::::::::::: keypoints
0307 
0308 - Snakemake allows one to run their data analyses and share them with others
0309 
0310 :::::::::::::::::::::::::::::::::::::::::::::::