<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://ccl.cse.nd.edu/feed.xml" rel="self" type="application/atom+xml"/><link href="https://ccl.cse.nd.edu/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-07-07T19:26:10+00:00</updated><id>https://ccl.cse.nd.edu/feed.xml</id><title type="html">The Cooperative Computing Lab</title><subtitle>Cooperative Computing Lab website: news, projects, publications, software, and community updates. </subtitle><entry><title type="html">Conda, Modules, and Containers: How to Manage Your Software Without Breaking the Cluster</title><link href="https://ccl.cse.nd.edu/blog/2026/containers-on-clusters/" rel="alternate" type="text/html" title="Conda, Modules, and Containers: How to Manage Your Software Without Breaking the Cluster"/><published>2026-07-07T17:00:00+00:00</published><updated>2026-07-07T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/containers-on-clusters</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/containers-on-clusters/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/containers-on-clusters/conda-logo-480.webp 480w,/assets/blog/2026/containers-on-clusters/conda-logo-800.webp 800w,/assets/blog/2026/containers-on-clusters/conda-logo-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/containers-on-clusters/conda-logo.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>If you are transitioning from running code on your personal laptop to using a high-performance computing (HPC) cluster, you are about to encounter a major cultural shock: <strong>you do not have <code class="language-plaintext highlighter-rouge">sudo</code> privileges.</strong> On your own machine, installing a new tool is as simple as running <code class="language-plaintext highlighter-rouge">sudo apt-get install</code>, <code class="language-plaintext highlighter-rouge">brew install</code>, or clicking an installer. On a shared cluster, these commands will reward you with a blunt <code class="language-plaintext highlighter-rouge">Permission denied</code>. This restriction isn’t just bureaucratic cruelty; it is a fundamental safety measure. In a shared environment with hundreds of users, giving everyone root access would lead to broken configurations, security breaches, and software chaos.</p> <p>So, how do you install the specific libraries, languages, and tools your research requires?</p> <p>HPC systems use three primary tools for software management: <strong>Environment Modules</strong>, <strong>Conda</strong>, and <strong>Containers</strong>. Each has its own strengths, and using them correctly will save you days of troubleshooting—and keep you from accidentally drawing the ire of the cluster administrators.</p> <h2 id="level-1-environment-modules-the-built-in-way">Level 1: Environment Modules (The Built-In Way)</h2> <p>Before you try to download or compile anything yourself, you should check if the cluster administrators have already installed it for you. HPC systems use an environment management tool called <strong>Modules</strong> to manage hundreds of pre-installed, highly optimized software packages.</p> <p>Because different researchers need different versions of the same software (e.g., Python 3.8 vs. Python 3.11, or different versions of CUDA), these packages sit dormant on the system until you explicitly ask for them.</p> <h3 id="key-commands-to-know">Key Commands to Know:</h3> <ul> <li><strong><code class="language-plaintext highlighter-rouge">module avail</code></strong>: Displays a massive list of all software packages pre-compiled on the cluster. You can filter this by adding a keyword, such as <code class="language-plaintext highlighter-rouge">module avail python</code> or <code class="language-plaintext highlighter-rouge">module avail gcc</code>.</li> <li><strong><code class="language-plaintext highlighter-rouge">module load &lt;package&gt;</code></strong>: Injects the chosen software into your current terminal session. For example, running <code class="language-plaintext highlighter-rouge">module load python/3.10.8</code> updates your environment so that typing <code class="language-plaintext highlighter-rouge">python</code> points directly to that specific version.</li> <li><strong><code class="language-plaintext highlighter-rouge">module list</code></strong>: Shows you which modules are currently active in your session.</li> <li><strong><code class="language-plaintext highlighter-rouge">module purge</code></strong>: Clears out all loaded modules, giving you a clean slate. This is incredibly useful if two software packages are conflicting with one another.</li> </ul> <h3 id="why-you-should-use-them">Why you should use them:</h3> <p>Modules are pre-compiled specifically for the cluster’s underlying hardware. This means they are often optimized to run significantly faster than a generic version you download off the internet. Best of all, they take up exactly zero bytes of your personal storage quota. <strong>Always check <code class="language-plaintext highlighter-rouge">module avail</code> first.</strong></p> <h2 id="level-2-conda--miniconda-the-user-space-way">Level 2: Conda / Miniconda (The User-Space Way)</h2> <p>What happens when you need a Python library or a niche scientific package that isn’t in the module list? For most data scientists and researchers, the immediate answer is <strong>Conda</strong>.</p> <p>Conda allows you to create completely isolated “sandboxes” (environments) where you can install any package version you want entirely within your user account. However, Conda comes with two massive pitfalls that frequently break beginners’ workflows.</p> <h3 id="pitfall-1-the-home-directory-trap">Pitfall #1: The Home Directory Trap</h3> <p>By default, when you run <code class="language-plaintext highlighter-rouge">conda create</code> or <code class="language-plaintext highlighter-rouge">conda install</code>, Conda downloads and extracts files into a hidden folder in your home directory (<code class="language-plaintext highlighter-rouge">~/.conda</code> or <code class="language-plaintext highlighter-rouge">~/.cache</code>).</p> <p>On almost all HPC clusters, your home directory (<code class="language-plaintext highlighter-rouge">/home/username</code>) has a <strong>strict, tiny storage quota</strong> (often between 10GB and 50GB) intended only for configuration files and source code. Because modern data science environments (especially those involving PyTorch or TensorFlow) can easily balloon to 10GB+ per environment, running a few installations can completely max out your storage quota. When this happens, you won’t even be able to log in or run basic commands.</p> <h4 id="the-fix-redirect-conda-to-storage-or-scratch">The Fix: Redirect Conda to Storage or Scratch</h4> <p>Before installing anything with Conda, tell it to store its environments and package caches in your lab’s high-capacity group folder or a fast scratch directory. You can do this by creating or editing a configuration file named <code class="language-plaintext highlighter-rouge">.condarc</code> in your home directory:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ~/.condarc</span>
<span class="na">envs_dirs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">/scratch/username/conda/envs</span>
  <span class="pi">-</span> <span class="s">/project/labname/shared_conda/envs</span>
<span class="na">pkgs_dirs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">/scratch/username/conda/pkgs</span>
</code></pre></div></div> <p>Replace <code class="language-plaintext highlighter-rouge">/scratch/username/</code> or <code class="language-plaintext highlighter-rouge">/project/labname/</code> with the actual high-capacity data paths provided by your cluster administrators.</p> <h3 id="pitfall-2-compiling-on-the-login-node">Pitfall #2: Compiling on the Login Node</h3> <p>When you log into a cluster, you land on a “login node.” This node is a shared hallway where hundreds of users are editing files and submitting jobs.</p> <p>Running a heavy command like <code class="language-plaintext highlighter-rouge">conda env create -f environment.yml</code> fires up intense CPU and memory processes to resolve dependencies and compile code. Doing this on a login node will slow down the terminal experience for everyone else on the cluster and will usually trigger an automated system script that kills your process mid-installation.</p> <h4 id="the-fix-use-an-interactive-compute-session">The Fix: Use an Interactive Compute Session</h4> <p>Whenever you need to build or modify a Conda environment, request a temporary compute node using SLURM’s interactive mode:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Request an interactive shell on a compute node for 1 hour</span>
srun <span class="nt">--tasks</span><span class="o">=</span>1 <span class="nt">--cpus-per-task</span><span class="o">=</span>2 <span class="nt">--mem</span><span class="o">=</span>8G <span class="nt">--time</span><span class="o">=</span>01:00:00 <span class="nt">--pty</span> bash
</code></pre></div></div> <p>Once your terminal transfers you to a compute node (e.g., <code class="language-plaintext highlighter-rouge">user@node042</code>), you can safely run your Conda installation commands without affecting anyone else.</p> <h2 id="level-3-containers-the-bulletproof-way">Level 3: Containers (The Bulletproof Way)</h2> <p>Sometimes, even Conda isn’t enough. If your workflow requires a complex web of system libraries, an entirely different operating system (like a specific version of Ubuntu when the cluster runs Rocky Linux), or you want to replicate an exact software pipeline from a published paper, you need <strong>containers</strong>.</p> <p>In the commercial tech world, <strong>Docker</strong> is the king of containers. However, Docker is fundamentally incompatible with shared HPC clusters because running Docker requires a background process (daemon) with root privileges. If you could run Docker on a cluster, you could easily bypass security and access other users’ data.</p> <h3 id="apptainer--singularity-to-the-rescue">Apptainer / Singularity to the Rescue</h3> <p>To solve this, the HPC community developed <strong>Apptainer</strong> (formerly known as Singularity). Apptainer allows you to run containers completely in “user space” without needing <code class="language-plaintext highlighter-rouge">sudo</code>.</p> <p>Instead of dealing with background daemons, Apptainer compresses an entire container environment into a single, portable file ending in <code class="language-plaintext highlighter-rouge">.sif</code> (Singularity Image Format).</p> <h3 id="key-commands-to-know-1">Key Commands to Know:</h3> <ol> <li><strong>Download and Convert a Docker Image:</strong> You can pull almost any public image directly from Docker Hub, and Apptainer will automatically translate it into an HPC-safe <code class="language-plaintext highlighter-rouge">.sif</code> file: <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apptainer pull ubuntu_python.sif docker://python:3.11-slim
</code></pre></div> </div> </li> <li><strong>Execute a Command Inside the Container:</strong> To run your code using the software isolated inside that container image, use <code class="language-plaintext highlighter-rouge">exec</code>: <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apptainer <span class="nb">exec </span>ubuntu_python.sif python3 my_script.py
</code></pre></div> </div> </li> <li><strong>Run an Interactive Shell Inside the Container:</strong> If you want to look around inside your containerized operating system environment: <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apptainer shell ubuntu_python.sif
</code></pre></div> </div> </li> </ol> <h3 id="why-you-should-use-them-1">Why you should use them:</h3> <p>Containers are the ultimate tool for <strong>scientific reproducibility</strong>. Once you find a container configuration that works, that <code class="language-plaintext highlighter-rouge">.sif</code> file will run exactly the same way on your university cluster, a national lab supercomputer, or a cloud instance five years from now.</p> <h2 id="summary-the-software-management-checklist">Summary: The Software Management Checklist</h2> <p>To keep your account active and the cluster running smoothly, run through this mental checklist every time you need new software:</p> <ol> <li><strong>Check <code class="language-plaintext highlighter-rouge">module avail</code> first.</strong> If it’s already installed by the system administrators, load it and move on.</li> <li><strong>Check your storage quotas.</strong> Run your cluster’s quota check command (like <code class="language-plaintext highlighter-rouge">myquota</code> or <code class="language-plaintext highlighter-rouge">lfs quota</code>) to ensure your home directory isn’t on the verge of filling up.</li> <li><strong>Configure your <code class="language-plaintext highlighter-rouge">.condarc</code> file.</strong> Never leave Conda on its default settings; always point your environment and package directories to a high-capacity scratch or group folder.</li> <li><strong>Never install software on a login node.</strong> Use <code class="language-plaintext highlighter-rouge">srun</code> to spin up an interactive compute session before compiling or running extensive installers.</li> <li><strong>Use Apptainer for complex pipelines.</strong> If a workflow requires intricate system dependencies or absolute reproducibility, skip the installation headaches entirely and look for a container image.</li> </ol>]]></content><author><name>Cooperative Computing Lab</name></author><category term="technical-articles"/><category term="hpc"/><category term="conda"/><category term="modules"/><category term="containers"/><category term="hpc"/><summary type="html"><![CDATA[Managing software on a shared supercomputer is completely different from your personal laptop. Here is how to use Modules, Conda, and Containers effectively without exhausting your storage or crashing the cluster.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/containers-on-clusters/conda-logo.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/containers-on-clusters/conda-logo.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">Getting Started with SLURM</title><link href="https://ccl.cse.nd.edu/blog/2026/getting-started-with-slurm/" rel="alternate" type="text/html" title="Getting Started with SLURM"/><published>2026-07-01T17:00:00+00:00</published><updated>2026-07-01T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/getting-started-with-slurm</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/getting-started-with-slurm/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/getting-started-with-slurm/Slurm_logo_sized-1875592753-480.webp 480w,/assets/blog/2026/getting-started-with-slurm/Slurm_logo_sized-1875592753-800.webp 800w,/assets/blog/2026/getting-started-with-slurm/Slurm_logo_sized-1875592753-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/getting-started-with-slurm/Slurm_logo_sized-1875592753.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>When you log into a university or national lab cluster, you aren’t logging directly into the powerful machines that run your code. Instead, you land on a “login node” shared with dozens of other users. To actually run your heavy computations, you have to request permission from a gatekeeper.</p> <p>On most High-Performance Computing (HPC) systems today, that gatekeeper is <strong>SLURM</strong> (Simple Linux Utility for Resource Management). Think of SLURM as a reservation system for a supercomputer. While it has a reputation for being complex, most day-to-day work comes down to a handful of basic commands. This guide covers the essential subset you need to get started: submitting jobs, monitoring them, stopping them when things go sideways, and understanding why they might be stuck in line.</p> <p><strong>One major caveat:</strong> SLURM is highly configurable, and every institution customizes it differently. Queue names, time limits, memory defaults, and specific resource rules vary from cluster to cluster. The commands here are standard SLURM, but you should always cross-reference them with your local site’s documentation.</p> <h2 id="submitting-a-job">Submitting a Job</h2> <p>You don’t run heavy scripts directly in the terminal on an HPC. Instead, you wrap your commands in a shell script and hand it over to SLURM using the <code class="language-plaintext highlighter-rouge">sbatch</code> command:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sbatch my_job.sh
</code></pre></div></div> <p>When SLURM accepts your job, it assigns it a unique <strong>Job ID</strong> and prints it to the screen:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Submitted batch job 847312
</code></pre></div></div> <p>You will use this number to track, manage, or cancel your job. A minimal, beginner-friendly job script looks like this:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c">#SBATCH --job-name=hpc-demo</span>
<span class="c">#SBATCH --output=hpc-demo-%j.out</span>
<span class="c">#SBATCH --error=hpc-demo-%j.err</span>
<span class="c">#SBATCH --time=04:00:00</span>
<span class="c">#SBATCH --ntasks=1</span>
<span class="c">#SBATCH --cpus-per-task=4</span>
<span class="c">#SBATCH --mem=8G</span>

<span class="c"># The commands below will run on the compute node once allocated:</span>
<span class="nb">echo</span> <span class="s2">"Starting my analysis..."</span>
python3 my_analysis_script.py <span class="nt">--input</span> data.csv
</code></pre></div></div> <p>The special <code class="language-plaintext highlighter-rouge">#SBATCH</code> lines at the top tell SLURM exactly what resources your code needs to run.</p> <h3 id="what-do-these-headers-mean">What do these headers mean?</h3> <ul> <li><code class="language-plaintext highlighter-rouge">--job-name</code>: A friendly nickname for your job so you can spot it easily in the queue.</li> <li><code class="language-plaintext highlighter-rouge">--output</code> and <code class="language-plaintext highlighter-rouge">--error</code>: Where your program’s text output and error messages should be saved. The <code class="language-plaintext highlighter-rouge">%j</code> is a variable that automatically fills in your unique Job ID, preventing different runs from overwriting each other’s logs.</li> <li><code class="language-plaintext highlighter-rouge">--time</code>: The maximum runtime allowed (Hours:Minutes:Seconds). If your job exceeds this, SLURM will stop it automatically.</li> <li><code class="language-plaintext highlighter-rouge">--cpus-per-task</code> and <code class="language-plaintext highlighter-rouge">--mem</code>: The requested number of CPU cores and total RAM (8 Gigabytes in this case).</li> </ul> <h2 id="passing-arguments-header-vs-command-line">Passing Arguments: Header vs. Command Line</h2> <p>Every <code class="language-plaintext highlighter-rouge">#SBATCH</code> line inside your script is identical to passing a flag directly to <code class="language-plaintext highlighter-rouge">sbatch</code> on the command line. These two methods accomplish the exact same thing:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Option A: All configuration is kept inside the script header</span>
sbatch my_job.sh

<span class="c"># Option B: Overriding the script's defaults from the command line</span>
sbatch <span class="nt">--time</span><span class="o">=</span>08:00:00 <span class="nt">--mem</span><span class="o">=</span>16G my_job.sh
</code></pre></div></div> <p>Command-line flags always take precedence over header directives. The best habit to build is putting your <strong>sensible defaults</strong> in the script header—the values you expect to use 90% of the time—and then overriding them on the command line for one-off experiments that require extra memory or longer runtimes.</p> <h2 id="watching-your-jobs">Watching Your Jobs</h2> <p>Once you submit a job, <code class="language-plaintext highlighter-rouge">squeue</code> is the command you will use most often. Running it without arguments shows every single job currently running on the entire cluster, which is overwhelming. Instead, filter it to show only your jobs:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>squeue <span class="nt">-u</span> <span class="nv">$USER</span>
</code></pre></div></div> <p>The output will look something like this:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>JOBID    PARTITION  NAME        USER     ST   TIME   NODES  NODELIST(REASON)
847312   general    hpc-demo    dthain   R    0:42   1      node047
847313   general    hpc-demo    dthain   PD   0:00   1      (Resources)
847314   general    hpc-demo    dthain   PD   0:00   1      (Priority)
</code></pre></div></div> <p>The <strong>ST (State)</strong> column tells you what your job is currently doing:</p> <ul> <li><strong><code class="language-plaintext highlighter-rouge">R</code> (Running):</strong> Your job is actively executing on a compute node.</li> <li><strong><code class="language-plaintext highlighter-rouge">PD</code> (Pending):</strong> Your job is waiting in line.</li> </ul> <p>The <strong><code class="language-plaintext highlighter-rouge">NODELIST(REASON)</code></strong> column explains <em>why</em> a pending job hasn’t started yet. <code class="language-plaintext highlighter-rouge">(Resources)</code> means the cluster is currently full and waiting for other users’ jobs to finish. <code class="language-plaintext highlighter-rouge">(Priority)</code> means your job is sitting behind higher-priority work in the queue.</p> <p>If you want to check when SLURM expects your job to start, add the <code class="language-plaintext highlighter-rouge">--start</code> flag:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>squeue <span class="nt">-u</span> <span class="nv">$USER</span> <span class="nt">--start</span>
</code></pre></div></div> <p><em>Note: SLURM’s start-time estimates are notoriously optimistic, as they assume every running job will use its maximum requested time. Treat these numbers as rough ballparks rather than guarantees.</em></p> <h2 id="cancelling-a-job">Cancelling a Job</h2> <p>If you realize your script has a bug, or you accidentally requested the wrong parameters, you can clear it out of the system using <code class="language-plaintext highlighter-rouge">scancel</code> and its Job ID:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scancel 847312
</code></pre></div></div> <p>To wipe the slate clean and cancel <strong>all</strong> of your jobs at once:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scancel <span class="nt">-u</span> <span class="nv">$USER</span>
</code></pre></div></div> <p>If you only want to clear out your waiting jobs while letting your active, running jobs finish safely, specify the pending state:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scancel <span class="nt">-u</span> <span class="nv">$USER</span> <span class="nt">-t</span> pending
</code></pre></div></div> <h2 id="checking-cluster-availability">Checking Cluster Availability</h2> <p>Before you submit a massive job, it is helpful to see how busy the cluster is. The <code class="language-plaintext highlighter-rouge">sinfo</code> command provides a snapshot of the available hardware pools (called “partitions”):</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sinfo
</code></pre></div></div> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PARTITION  AVAIL  TIMELIMIT   NODES  STATE   NODELIST
general*   up     7-00:00:00  12     idle    node[001-012]
gpu        up     2-00:00:00  4      mix     gpu[01-04]
debug      up     0-01:00:00  2      idle    node[013-014]
</code></pre></div></div> <p>Pay attention to the <strong>STATE</strong> column:</p> <ul> <li><strong><code class="language-plaintext highlighter-rouge">idle</code>:</strong> The nodes are completely free and ready for work.</li> <li><strong><code class="language-plaintext highlighter-rouge">mix</code>:</strong> Some CPU cores on these nodes are busy, but others are still available.</li> <li><strong><code class="language-plaintext highlighter-rouge">alloc</code>:</strong> The nodes are completely full.</li> <li><strong><code class="language-plaintext highlighter-rouge">drain</code>:</strong> The nodes have been taken offline by administrators for maintenance.</li> </ul> <h2 id="inspecting-a-live-or-stuck-job">Inspecting a Live or Stuck Job</h2> <p>For detailed troubleshooting on a specific job, <code class="language-plaintext highlighter-rouge">scontrol show job</code> will output a comprehensive diagnostic record:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scontrol show job 847312
</code></pre></div></div> <p>The output is quite long, but look closely for <code class="language-plaintext highlighter-rouge">JobState</code>, <code class="language-plaintext highlighter-rouge">Reason</code>, and <code class="language-plaintext highlighter-rouge">TRES</code> (the exact resource request). If a job has been pending for days, the <code class="language-plaintext highlighter-rouge">Reason</code> field will often reveal if it is blocked by an administrative hold, an impossible resource request, or standard queue traffic.</p> <h2 id="checking-what-completed-jobs-actually-used">Checking What Completed Jobs <em>Actually</em> Used</h2> <p>One of the biggest mistakes beginners make is overestimating how much memory or time their code needs. Requesting too many resources forces your job to wait in line much longer than necessary.</p> <p>To look back at a completed job and see its actual resource footprint, use <code class="language-plaintext highlighter-rouge">sacct</code>:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sacct <span class="nt">-j</span> 847312 <span class="nt">--format</span><span class="o">=</span>JobID,JobName,State,Elapsed,MaxRSS,ReqMem
</code></pre></div></div> <p>The <strong><code class="language-plaintext highlighter-rouge">MaxRSS</code></strong> field shows the peak amount of RAM your job actually consumed during its run. Compare this against <strong><code class="language-plaintext highlighter-rouge">ReqMem</code></strong> (what you requested). If your job only used 2GB of RAM but you requested 32GB, you are artificially delaying your own queue times. Aim to request roughly 20–30% more memory than your historical peak to keep your queue times short while avoiding out-of-memory errors.</p> <h2 id="a-few-silent-pitfalls-to-avoid">A Few Silent Pitfalls to Avoid</h2> <p>SLURM enforces local cluster rules strictly, and it often does so silently. Keep these four items in mind as you review your institution’s specific user guide:</p> <ul> <li><strong>Partition Time Caps:</strong> Most cluster queues have hard maximum runtimes (e.g., a 24-hour limit). If you ask for 48 hours on a 24-hour queue, SLURM will reject your job the second you attempt to submit it.</li> <li><strong>Account Flags:</strong> Many institutions require you to specify a billing project or account via <code class="language-plaintext highlighter-rouge">--account=my_lab_group</code>. If you omit this, your job may fail instantly or be assigned to a lowest-priority pool.</li> <li><strong>Silent Memory Defaults:</strong> If you don’t specify a <code class="language-plaintext highlighter-rouge">--mem</code> flag, SLURM will assign you a default value per core. On some clusters, this default can be incredibly small (like 1GB), causing your program to crash unexpectedly with an <code class="language-plaintext highlighter-rouge">OUT_OF_MEMORY</code> error. Always declare your memory explicitly.</li> <li><strong>Job Arrays for Repetitive Work:</strong> If you need to run the exact same analysis script over 50 different data files, do not run <code class="language-plaintext highlighter-rouge">sbatch</code> 50 separate times. Instead, look into <strong>Job Arrays</strong> using <code class="language-plaintext highlighter-rouge">sbatch --array=1-50</code>. It allows the scheduler to handle your workload as a single cohesive unit, saving your sanity and keeping the cluster running smoothly.</li> </ul>]]></content><author><name>Cooperative Computing Lab</name></author><category term="technical-articles"/><category term="slurm"/><category term="tutorial"/><category term="hpc"/><summary type="html"><![CDATA[SLURM is the job scheduler running on most HPC clusters today. Here are the commands and habits that get you productive quickly as an end-user, without having to read the entire manual first.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/getting-started-with-slurm/Slurm_logo_sized-1875592753.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/getting-started-with-slurm/Slurm_logo_sized-1875592753.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">Work Queue Insights: Practical Debugging on HPC Systems</title><link href="https://ccl.cse.nd.edu/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/" rel="alternate" type="text/html" title="Work Queue Insights: Practical Debugging on HPC Systems"/><published>2026-06-23T17:00:00+00:00</published><updated>2026-06-23T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/Work-Queue-Insights-480.webp 480w,/assets/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/Work-Queue-Insights-800.webp 800w,/assets/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/Work-Queue-Insights-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/Work-Queue-Insights.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>Debugging distributed systems on HPC has its own rhythm. The feedback loop is slow, the machines are shared, and the error you are chasing may only appear under load, across nodes, or after a scheduler delay you cannot reproduce locally. This note grew out of <a href="https://github.com/cooperative-computing-lab/cctools/issues/4415">issue #4415</a>: a segmentation fault (SIGSEGV) in <code class="language-plaintext highlighter-rouge">task_min_resources</code> inside <a href="https://github.com/cooperative-computing-lab/cctools/tree/master/work_queue/src"><code class="language-plaintext highlighter-rouge">work_queue.c</code></a>, triggered by Makeflow running a large parallel workflow in Work Queue mode.</p> <p>The habits below are less about any single tool and more about discipline: keeping your changes visible, your experiments small, and your teammates in the loop. They apply equally whether you are working in <a href="https://github.com/cooperative-computing-lab/cctools/tree/master/work_queue/src"><code class="language-plaintext highlighter-rouge">work_queue.c</code></a> or anywhere else in a large C codebase running on a shared cluster.</p> <h2 id="talk-to-your-teammates-early-and-often">Talk to your teammates early and often</h2> <p>The most underrated debugging tool is a colleague. When you hit a wall the instinct is to keep grinding alone until you have something to show. Resist it. A two-sentence description of what you changed and where you are stuck surfaces assumptions you did not know you were making before you spend a day looking in the wrong function.</p> <p>Post a short note to your team channel whenever you switch gears. If you are about to run a batch of SLURM jobs to test a hypothesis, say so. Someone may have already run that experiment, or may know that the catalog tick interval is configurable and could be shortened for testing.</p> <p>The flip side: when someone asks for help, give them the full picture up front. Paste the Valgrind output, the backtrace, and a short description of what you have already tried. “Here is what I know” cuts the back-and-forth in half.</p> <h2 id="put-your-changes-somewhere-others-can-see-them">Put your changes somewhere others can see them</h2> <p>Debugging on HPC usually means modifying source, rebuilding, and staging a binary on a shared login node where the next person has no idea what you changed. The fix is obvious but easy to skip when you are in a hurry: commit and push early, to a branch, a fork, or a scratch repository your colleagues can actually reach.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout <span class="nt">-b</span> debug/task-min-resources-segfault
<span class="c"># edit work_queue/src/work_queue.c</span>
git add work_queue/src/work_queue.c
git commit <span class="nt">-m</span> <span class="s2">"add null check before task_min_resources dereferences ready list node"</span>
git push origin debug/task-min-resources-segfault
</code></pre></div></div> <p>A pushed branch does several things at once. It gives collaborators a URL to look at instead of a terminal paste. It lets them check out your exact tree, reproduce the build, and point out something you missed. And it is an automatic checkpoint: if <code class="language-plaintext highlighter-rouge">make clean</code> goes sideways or a node corrupts your scratch directory, the work is not gone. Uncommitted changes on a cluster login node are one bad disk event away from disappearing.</p> <h2 id="keep-a-running-log-of-what-you-changed-and-why">Keep a running log of what you changed and why</h2> <p>Segfault hunts often take days or weeks. Memory is not reliable across that span. Keep a plain text file, a section in the PR description, or a lab notebook — and update it every time you try something:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2026-06-09  opened #4415; Valgrind log attached; fault site is work_queue.c:8174 in task_min_resources
2026-06-10  added null check on q-&gt;ready_list head before dereferencing — crash still appears on run 3 of 5
2026-06-11  hypothesis: node is removed from the ready list but list_next still returns it; added fprintf around list traversal
2026-06-12  confirmed: stale pointer survives across catalog tick; looking at where nodes are freed vs unlinked
</code></pre></div></div> <p>This log is not for posterity. It is for you, tomorrow morning, when you cannot remember why you commented out that block. It is also what you paste when you ask a teammate for help.</p> <h2 id="scale-down-before-you-scale-out">Scale down before you scale out</h2> <p>Submitting a five-hundred-task Makeflow workflow to test a one-line fix is one of the most reliable ways to waste an afternoon. Queue wait times are unpredictable, the logs from hundreds of workers are noisy, and if the fix is wrong you have burned allocation budget and still do not know why.</p> <p>The better approach is to find or construct the smallest possible reproducer. Once the small case reproduces reliably, you have a debuggable target. Only when that case is clean do you run at scale to confirm nothing regresses. This discipline also makes it far easier to share a reproducer with a teammate: “clone, run these two commands, watch it crash” is a much better bug report than “submit two hundred SLURM jobs and search the logs.”</p> <h2 id="instrument-the-code-before-you-reach-for-a-debugger">Instrument the code before you reach for a debugger</h2> <p>Before attaching GDB or running Valgrind, the fastest thing you can do is add targeted <code class="language-plaintext highlighter-rouge">fprintf</code> calls to verify that your fix is actually being reached. This sounds obvious, and yet the most common source of “my patch does nothing” confusion is that the code path you edited is never entered for the input you are testing.</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* In work_queue.c, before the dereference that Valgrind flagged */</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[DEBUG] task_min_resources: checking node %p on ready list</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">t</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">t</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[DEBUG] task_min_resources: null node encountered — skipping</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="k">continue</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[DEBUG] task_min_resources: node task_id=%d resources_requested=%p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
        <span class="n">t</span><span class="o">-&gt;</span><span class="n">taskid</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">t</span><span class="o">-&gt;</span><span class="n">resources_requested</span><span class="p">);</span>
</code></pre></div></div> <p>Write to <code class="language-plaintext highlighter-rouge">stderr</code>, not <code class="language-plaintext highlighter-rouge">stdout</code>, which may be buffered or redirected by the framework. Add prints before and after the critical section. If the “before” line appears but the “after” line does not, you found the crash site. If neither appears, the function is not being called — which means the bug is upstream of where you thought it was, and you just saved yourself an hour of reading the wrong code.</p> <p>If the prints confirm your fix is being reached but the crash keeps happening, the root cause is elsewhere: probably the point where the stale node enters the list, not the point where it is dereferenced. Move the instrumentation upstream.</p> <p>Gate these prints behind a compile-time flag before merging so they do not pollute production builds:</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef WQ_DEBUG_TASK_RESOURCES
</span><span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[DEBUG] task_min_resources: node %p task_id=%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
        <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">t</span><span class="p">,</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">taskid</span><span class="p">);</span>
<span class="cp">#endif
</span></code></pre></div></div> <p>Pass <code class="language-plaintext highlighter-rouge">-DWQ_DEBUG_TASK_RESOURCES</code> in <code class="language-plaintext highlighter-rouge">CFLAGS</code> to turn them on; a normal build leaves no noise.</p> <h2 id="reach-for-gdb-and-valgrind-when-instrumentation-is-not-enough">Reach for GDB and Valgrind when instrumentation is not enough</h2> <p>When <code class="language-plaintext highlighter-rouge">fprintf</code> tells you where the crash is but not why, it is time to bring in a real debugger. For a crash you can reproduce locally, GDB is the first stop:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Build with debug symbols and no optimization so variables are readable</span>
./configure <span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"-g -O0"</span>
make <span class="nt">-C</span> work_queue/src
 
<span class="c"># Run until it crashes, then inspect the frame</span>
gdb <span class="nt">--args</span> work_queue_worker <span class="nt">-d</span> all localhost 9123
<span class="o">(</span>gdb<span class="o">)</span> run
<span class="o">(</span>gdb<span class="o">)</span> bt                        <span class="c"># full backtrace at the crash point</span>
<span class="o">(</span>gdb<span class="o">)</span> frame 2                   <span class="c"># switch to the frame inside task_min_resources</span>
<span class="o">(</span>gdb<span class="o">)</span> p t                       <span class="c"># is the pointer null or garbage?</span>
<span class="o">(</span>gdb<span class="o">)</span> p t-&gt;resources_requested  <span class="c"># does the struct look sane?</span>
<span class="o">(</span>gdb<span class="o">)</span> watch t-&gt;taskid           <span class="c"># set a watchpoint to catch when this field changes</span>
</code></pre></div></div> <p>Valgrind slows execution by roughly ten to twenty times, so pair it with the scaled-down reproducer from the previous section. A handful of short tasks that trigger the ready-list traversal during a catalog tick is a much better target than a full production workflow.</p> <p>One practical note: you generally cannot run GDB interactively inside a SLURM job. If the bug only appears at scale, use Work Queue’s <code class="language-plaintext highlighter-rouge">-d all</code> flag to write a verbose trace to a shared filesystem path that both the execute node and your login node can read:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>work_queue_worker <span class="nt">-d</span> all <span class="nt">-o</span> /cephfs/group/myproject/wq-logs/worker-<span class="si">$(</span><span class="nb">date</span> +%Y%m%d%H%M%S<span class="si">)</span>.log <span class="se">\</span>
  localhost 9123
</code></pre></div></div> <p>Then reconstruct a local reproducer from what the trace tells you, and use GDB on that.</p> <h2 id="read-the-logs-that-are-already-there">Read the logs that are already there</h2> <p>Before adding instrumentation or firing up a debugger, check what Work Queue already records. The <code class="language-plaintext highlighter-rouge">-d all</code> flag produces a structured trace across subsystems — scheduling decisions, catalog updates, task state transitions — that often answers the question before you write a single <code class="language-plaintext highlighter-rouge">fprintf</code>.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Watch the debug stream in real time on a local test run</span>
work_queue_worker <span class="nt">-d</span> all localhost 9123 2&gt;&amp;1 | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"task_min</span><span class="se">\|</span><span class="s2">ready</span><span class="se">\|</span><span class="s2">segfault</span><span class="se">\|</span><span class="s2">null</span><span class="se">\|</span><span class="s2">error"</span>
 
<span class="c"># After a cluster run, search a saved log</span>
<span class="nb">grep</span> <span class="nt">-n</span> <span class="s2">"task_min_resources</span><span class="se">\|</span><span class="s2">ready_list"</span> worker-20260609.log | <span class="nb">head</span> <span class="nt">-40</span>
</code></pre></div></div> <p>The log format uses timestamps and subsystem tags, so you can narrow a twenty-thousand-line trace to the thirty lines around the catalog tick event without much effort. For the #4415 bug in particular, looking for the catalog update message immediately before the crash narrows the search window considerably.</p> <h2 id="turn-on-every-debug-flag-and-log-everything">Turn on every debug flag and log everything</h2> <p>When you are stuck, err heavily on the side of logging too much rather than too little. A verbose log that you have to <code class="language-plaintext highlighter-rouge">grep</code> through is far better than a quiet one that leaves you guessing. The more you record, the more likely it is that the crash site, the bad pointer, and the sequence of events leading to it are all sitting in the file waiting for you.</p> <p>CCTools exposes this through the <code class="language-plaintext highlighter-rouge">-d</code> flag, which accepts one or more subsystem names. The relevant ones for a Work Queue + Makeflow investigation are <code class="language-plaintext highlighter-rouge">wq</code> (Work Queue task scheduling and worker communication), <code class="language-plaintext highlighter-rouge">batch</code> (the batch system layer that submits SLURM or Condor jobs), <code class="language-plaintext highlighter-rouge">rmon</code> (the resource monitor that measures cores, memory, and disk per task), and <code class="language-plaintext highlighter-rouge">makeflow</code> (the Makeflow DAG engine, which covers parsing, lexing, and the run loop). Pass them all:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Worker: turn on every relevant subsystem and save to a named file</span>
work_queue_worker <span class="nt">-d</span> all <span class="nt">-o</span> worker.log localhost 9123 &amp;
 
<span class="c"># Makeflow manager: same idea, log to a separate file</span>
makeflow <span class="nt">-T</span> wq <span class="nt">-d</span> wq,batch,rmon,makeflow <span class="nt">-o</span> makeflow.log mini.makeflow
</code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">-d all</code> is the blunt instrument — it sets every bit in the flag word and includes subsystems you probably do not care about on this run. That is fine. The extra volume costs you nothing except disk space, and the subsystem tags in the log make it easy to filter later. If the log grows large enough to be unwieldy, narrow it down once you know which subsystem to focus on; start with <code class="language-plaintext highlighter-rouge">all</code> and restrict from there.</p> <p>On the cluster, where you cannot read <code class="language-plaintext highlighter-rouge">stderr</code> interactively, always pair <code class="language-plaintext highlighter-rouge">-d all</code> with <code class="language-plaintext highlighter-rouge">-o</code> pointing at a shared filesystem path that your login node can reach after the job finishes. Give each job a unique filename so concurrent workers do not overwrite each other:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>work_queue_worker <span class="nt">-d</span> all <span class="se">\</span>
  <span class="nt">-o</span> /cephfs/group/myproject/wq-logs/worker-<span class="si">$(</span><span class="nb">date</span> +%Y%m%d%H%M%S<span class="si">)</span>-<span class="nv">$$</span>.log <span class="se">\</span>
  localhost 9123
</code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">$$</code> expands to the worker process id on the execute node, which is a cheap uniquifier on top of the timestamp. Once the logs land, search them as a unit:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find the catalog tick and the crash window across all workers in a session</span>
<span class="nb">grep</span> <span class="nt">-h</span> <span class="s2">"task_min_resources</span><span class="se">\|</span><span class="s2">catalog</span><span class="se">\|</span><span class="s2">ready_list</span><span class="se">\|</span><span class="s2">SIGSEGV"</span> <span class="se">\</span>
  /cephfs/group/myproject/wq-logs/worker-20260609-<span class="k">*</span>.log <span class="se">\</span>
  | <span class="nb">sort</span> <span class="nt">-k1</span>,2 | less
</code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">-h</code> flag suppresses the filename prefix so the timestamps sort cleanly. Sorting by the first two fields — date and time — stitches the per-worker logs into a single chronological view of what the whole pool was doing in the seconds before the crash. That cross-worker timeline is often what finally makes the bug obvious.</p> <p>One warning: do not turn on <code class="language-plaintext highlighter-rouge">--debug-workers</code> in <code class="language-plaintext highlighter-rouge">work_queue_factory</code> and also pass <code class="language-plaintext highlighter-rouge">-d all</code> through <code class="language-plaintext highlighter-rouge">--extra-options</code> at the same time. The factory-side flag already appends <code class="language-plaintext highlighter-rouge">-d all -o worker.&lt;n&gt;.log</code> to every submission; doubling up means every worker writes two interleaved logs to slightly different paths and you end up reading redundant output. Pick one approach per session.</p> <h2 id="the-quick-checklist-before-every-cluster-run">The quick checklist before every cluster run</h2> <p>Before you fire off a batch job, spend sixty seconds on these:</p> <ul> <li>Did you rebuild and reinstall after your last edit? A stale binary is the most common source of “my fix does nothing.”</li> <li>Is the debug log going to a path that exists and is writable from the execute node?</li> <li>Is the job small enough that you will get a result in under ten minutes, or do you have a good reason to go bigger right now?</li> <li>Did you commit your current state so that whatever happens on the cluster, the code is recoverable?</li> <li>Did you tell a teammate what you are about to test, so they can flag it if they know something relevant?</li> </ul> <p>None of these take long. Together they prevent the most common ways a cluster debugging session turns into a wasted afternoon.</p>]]></content><author><name>Cooperative Computing Lab</name></author><category term="technical-articles"/><category term="work_queue"/><category term="makeflow"/><category term="slurm"/><category term="debugging"/><category term="hpc"/><summary type="html"><![CDATA[A segfault in task_min_resources taught us a few things about staying sane while debugging on HPC systems. Here are the habits that keep you from burning hours waiting on a large workflow when a three-task smoke test would have told you the same thing.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/Work-Queue-Insights.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/work-queue-insights-practical-debugging-on-hpc-systems/Work-Queue-Insights.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">HTC26 Experience</title><link href="https://ccl.cse.nd.edu/blog/2026/htc26-experience/" rel="alternate" type="text/html" title="HTC26 Experience"/><published>2026-06-17T17:00:00+00:00</published><updated>2026-06-17T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/htc26-experience</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/htc26-experience/"><![CDATA[<p>One of the graduate students from our lab, Lax, attended <a href="https://agenda.hep.wisc.edu/event/2432/">HTC26</a> for the first time. While there, he had the chance to connect with researchers from universities, industry, and national laboratories across the HPC community. Here are his thoughts on the experience:</p> <div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/htc26-experience/throughput-2026-banners-480.webp 480w,/assets/blog/2026/htc26-experience/throughput-2026-banners-800.webp 800w,/assets/blog/2026/htc26-experience/throughput-2026-banners-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/htc26-experience/throughput-2026-banners.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>Last week, I had the opportunity to attend <strong>Throughput Computing Week 2026</strong> in Madison, Wisconsin, organized by the Center for High Throughput Computing (CHTC). I participated in the conference on Wednesday and Thursday and came away with a deeper appreciation for the diverse ways high-throughput computing is impacting research across disciplines.</p> <p>One of the highlights of the event was attending my advisor <strong>Dr. Douglas Thain’s</strong> talk on Thursday, <em>“Wrangling Massive Task Graphs with VineReduce.”</em> The presentation showcased new approaches for managing large-scale computational workflows and demonstrated the continued evolution of distributed computing systems.</p> <p>Beyond the technical sessions, one of the most rewarding aspects of the conference was meeting researchers from a wide range of scientific domains. I spoke with teams working in space research who use HTCondor to process massive amounts of satellite-generated terrain data. Other researchers shared how high-throughput computing is accelerating genome analysis in biology and enabling large-scale weather modeling that helps civil engineers design more efficient roads and buildings.</p> <p>Among the many talks, I was particularly fascinated by discussions around <strong>Pelican</strong> and its role in distributed data storage and access. I also learned about emerging challenges surrounding next-generation hardware accelerators and AI-focused chips. Researchers and computing teams from universities across the country discussed how they are balancing growing computational demands through cloud resources, strategic infrastructure investments, and cost optimization techniques.</p> <p>As artificial intelligence continues to reshape science and engineering, the conference highlighted how computing infrastructures are evolving alongside it. From data-intensive scientific discovery to large-scale AI workloads, the conversations at Throughput Computing Week reinforced how rapidly the world of research computing is changing—and how important collaboration across disciplines will be in navigating that future.</p> <div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/htc26-experience/image-480.webp 480w,/assets/blog/2026/htc26-experience/image-800.webp 800w,/assets/blog/2026/htc26-experience/image-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/htc26-experience/image.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/htc26-experience/IMG_0168-480.webp 480w,/assets/blog/2026/htc26-experience/IMG_0168-800.webp 800w,/assets/blog/2026/htc26-experience/IMG_0168-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/htc26-experience/IMG_0168.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div>]]></content><author><name>Cooperative Computing Lab</name></author><category term="news"/><category term="news"/><summary type="html"><![CDATA[One of our graduate students attended HTC26 for the first time, here is what he learned.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/htc26-experience/throughput-2026-banners.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/htc26-experience/throughput-2026-banners.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">Alan Visits Fermilab</title><link href="https://ccl.cse.nd.edu/blog/2026/alan-visits-fermilab/" rel="alternate" type="text/html" title="Alan Visits Fermilab"/><published>2026-06-10T17:00:00+00:00</published><updated>2026-06-10T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/alan-visits-fermilab</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/alan-visits-fermilab/"><![CDATA[<p>In mid-May, <strong>Alan Rodrigues</strong> headed to Fermilab for the <strong>Scientific Workflow Management Cross-Experiment Retreat</strong>! 🚀</p> <p>This event brought together experts from 10 different experiments to align on shared challenges and collaborate on community solutions. Beyond establishing a common technical language, we mapped out key focus areas for the future:</p> <ul> <li>Resource optimization: Improving execution behavior and utilization.</li> <li>Data-aware late binding: Navigating data locality and streaming.</li> <li>Request, provenance &amp; validation: Exploring unified standards for workflow interfaces.</li> <li>AI in operations: Leveraging AI assistance in complex distributed systems.</li> </ul> <p>The conversation is just getting started. If you’re interested in the evolution of Workflow/Workload Management and community standards, we’d love for you to join us! Connect via our <a href="https://mattermost.web.cern.ch/signup_user_complete/?id=1qzfshcjbbr4z81w6yrr4qbtic&amp;md=link&amp;sbr=su">Mattermost channel</a></p> <div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/alan-visits-fermilab/20260514-_DSC1302-480.webp 480w,/assets/blog/2026/alan-visits-fermilab/20260514-_DSC1302-800.webp 800w,/assets/blog/2026/alan-visits-fermilab/20260514-_DSC1302-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/alan-visits-fermilab/20260514-_DSC1302.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="Alan Visits Fermilab" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div>]]></content><author><name>Cooperative Computing Lab</name></author><category term="news"/><category term="news"/><category term="conference"/><summary type="html"><![CDATA[At the Scientific Workflow Management Cross-Experiment Retreat at Fermilab, Alan Rodrigues joined experts from 10 experiments to tackle shared workflow management challenges and define future priorities in resource optimization, data-aware scheduling, workflow standards, and AI-assisted operations.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/alan-visits-fermilab/20260514-_DSC1302.jpg"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/alan-visits-fermilab/20260514-_DSC1302.jpg" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">SciWIND at IPDPS 2026</title><link href="https://ccl.cse.nd.edu/blog/2026/ipdps2026-highlights/" rel="alternate" type="text/html" title="SciWIND at IPDPS 2026"/><published>2026-06-03T17:00:00+00:00</published><updated>2026-06-03T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/ipdps2026-highlights</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/ipdps2026-highlights/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/ipdps2026-highlights/ipdps-hero-image-480.webp 480w,/assets/blog/2026/ipdps2026-highlights/ipdps-hero-image-800.webp 800w,/assets/blog/2026/ipdps2026-highlights/ipdps-hero-image-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/ipdps2026-highlights/ipdps-hero-image.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="IPDPS 2026" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>This May CCL third-year PhD student <strong>Jin Zhou</strong> traveled to New Orleans for <strong><a href="https://www.ipdps.org/">IPDPS 2026</a></strong>, the 40th IEEE International Parallel &amp; Distributed Processing Symposium, held at the Marriott on Canal Street from May 25 to 29. He presented our paper <a href="https://ccl.cse.nd.edu/assets/paper/pdf/sciwind-ipdps-2026.pdf"><strong>SciWIND: Effectively Exploiting Node-Local Storage for Data-Intensive High-Energy Physics Workflows</strong></a>, which looks at how to use node-local scratch more deliberately when large HEP workflows run on opportunistic clusters and workers fail mid-run. The talk was a nice cap on a line of work the lab has been pushing through TaskVine and our HEP collaborations, and it was good to put the system in front of people who live with scheduling, storage, and workflow engines every day.</p> <p>Between sessions Jin followed the <strong><a href="https://ssl.linklings.net/conferences/ipdps/ipdps2026_program/views/at_a_glance.html">conference program</a></strong>: tutorials and workshops on the first two days, then the main track, keynotes, and plenty of hallway conversations. IPDPS still has that familiar mix of parallel algorithms, distributed systems, and applications at scale. AI was clearly a hot topic this year, showing up in keynotes, panels, and hallway chats about training and inference at scale. A few questions after the SciWIND talk turned into longer conversations about eviction recovery, disk pressure on shared filesystems, and where workflow runtimes should own policy versus leave it to the user. New Orleans helped too, with late walks along Canal Street and coffee between sessions that made the week feel less like a sprint and more like a real meeting of the community.</p> <div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/ipdps2026-highlights/conference-photo-480.webp 480w,/assets/blog/2026/ipdps2026-highlights/conference-photo-800.webp 800w,/assets/blog/2026/ipdps2026-highlights/conference-photo-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/ipdps2026-highlights/conference-photo.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="Jin Zhou at IPDPS 2026" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>Thanks to everyone who came to the SciWIND session and to the IPDPS organizers for another smooth run. We are glad the paper is out in the proceedings and happy to keep the conversation going with groups wrestling with the same storage and resilience headaches in production science workflows.</p>]]></content><author><name>Cooperative Computing Lab</name></author><category term="news"/><category term="news"/><category term="conference"/><summary type="html"><![CDATA[CCL third-year PhD student Jin Zhou traveled to IPDPS 2026 in New Orleans to present SciWIND on node-local storage for data-intensive high-energy physics workflows.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/ipdps2026-highlights/ipdps-hero-image.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/ipdps2026-highlights/ipdps-hero-image.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">CCL Team at GCASR 2026</title><link href="https://ccl.cse.nd.edu/blog/2026/gcasr/" rel="alternate" type="text/html" title="CCL Team at GCASR 2026"/><published>2026-05-12T17:00:00+00:00</published><updated>2026-05-12T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/gcasr</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/gcasr/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/gcasr/hall-480.webp 480w,/assets/blog/2026/gcasr/hall-800.webp 800w,/assets/blog/2026/gcasr/hall-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/gcasr/hall.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="GCASR venue" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>This week our lab was in Chicago for <strong>GCASR 2026</strong>—the <strong>Greater Chicago Area Systems Research Workshop</strong> (<a href="https://gcasr.org">gcasr.org</a>), a regional gathering focused on computer systems, from operating systems and distributed infrastructure to the tools that make large-scale science tractable. It is a friendly venue for students and faculty to share work in progress, compare notes on real systems problems, and meet peers from departments and labs around the Midwest.</p> <p>The CCL makes this trip every year: Chicago is home base for GCASR, and showing up has become part of how we stay plugged into the systems community between the bigger conference cycles.</p> <p>Each student on the team presented a research poster, as we typically do at this venue. The poster floor was busy, with good questions, fast feedback, and plenty of hallway conversations that do not fit on a slide deck. Between sessions, people attended invited talks and panels, swapped implementation details, and followed threads from scheduling and storage to workflows and AI-facing infrastructure.</p> <div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/gcasr/group-photo-480.webp 480w,/assets/blog/2026/gcasr/group-photo-800.webp 800w,/assets/blog/2026/gcasr/group-photo-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/gcasr/group-photo.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="CCL at GCASR" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>Thanks to everyone who stopped by our posters and to the organizers for another well-run GCASR. We appreciated the insights, the introductions, and the chance to catch up with colleagues in person. We look forward to seeing familiar faces at GCASR 2027!</p>]]></content><author><name>Cooperative Computing Lab</name></author><category term="news"/><category term="news"/><category term="workshop"/><category term="gcasr"/><summary type="html"><![CDATA[The CCL traveled to Chicago for the Greater Chicago Area Systems Research Workshop (GCASR). Students presented posters, caught invited talks, and connected with the local systems community.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/gcasr/hall.jpg"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/gcasr/hall.jpg" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">TaskVine Insights: Shipping Worker Builds to Remote Nodes and Debugging There</title><link href="https://ccl.cse.nd.edu/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/" rel="alternate" type="text/html" title="TaskVine Insights: Shipping Worker Builds to Remote Nodes and Debugging There"/><published>2026-05-05T17:00:00+00:00</published><updated>2026-05-05T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/TaskVine-Insights-480.webp 480w,/assets/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/TaskVine-Insights-800.webp 800w,/assets/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/TaskVine-Insights-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/TaskVine-Insights.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>Once you are hacking <a href="https://github.com/cooperative-computing-lab/cctools/tree/master/taskvine/src/worker"><code class="language-plaintext highlighter-rouge">vine_worker</code></a> in C and feeding workers through HTCondor, two boring problems show up over and over: the pool still runs yesterday’s binary, and your printfs never appear next to the Python manager. This note is about staging the binary you meant to ship and getting worker-side trace somewhere useful. For the repo layout start with <a href="https://ccl.cse.nd.edu/blog/2026/taskvine-insights-a-beginners-map-to-the-cctools-codebase/">A Beginner’s Map to the CCTools Codebase</a>; for the usual <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> / <code class="language-plaintext highlighter-rouge">vine_factory</code> workflow see <a href="https://ccl.cse.nd.edu/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/">Submitting Workers to a Cluster</a>.</p> <p>None of that changes the basic fact that a live worker runs whatever was staged when the job went in, not necessarily whatever <code class="language-plaintext highlighter-rouge">make install</code> just dropped into <code class="language-plaintext highlighter-rouge">$PATH</code>. To pick up a new build you still have to drain the old worker jobs, stop a factory that is holding a stale scratch copy if one is running, install, and submit again.</p> <h2 id="the-install-that-actually-reaches-htcondor">The install that actually reaches HTCondor</h2> <p>After <code class="language-plaintext highlighter-rouge">./configure</code>, we still do the blunt rebuild from <a href="https://github.com/cooperative-computing-lab/cctools/tree/master/taskvine/src"><code class="language-plaintext highlighter-rouge">taskvine/src</code></a> when we want zero ambiguity:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make clean <span class="o">&amp;&amp;</span> make <span class="o">&amp;&amp;</span> make <span class="nb">install</span>
</code></pre></div></div> <p>On your own machine you probably have a <code class="language-plaintext highlighter-rouge">vine_worker</code> running already. Stop it before <code class="language-plaintext highlighter-rouge">make clean &amp;&amp; make &amp;&amp; make install</code>. Overwriting the on-disk binary while the process still has it mapped is a good way to take a nose dive, sometimes straight into a segfault, and whatever tasks it was holding will go with it. Bring the worker back up against the manager after the install.</p> <p>On the cluster there is another wrinkle. <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> drops a copy of <code class="language-plaintext highlighter-rouge">vine_worker</code> into its submit directory when you run it; <code class="language-plaintext highlighter-rouge">vine_factory</code> copies whatever <code class="language-plaintext highlighter-rouge">vine_worker</code> resolves to (unless you pointed it at <code class="language-plaintext highlighter-rouge">--worker-binary</code>) into <code class="language-plaintext highlighter-rouge">-S,--scratch-dir</code> once at startup, and every batch job runs <code class="language-plaintext highlighter-rouge">./vine_worker</code> out of that directory. Tweaking <code class="language-plaintext highlighter-rouge">$PATH</code> later while the factory is still running does nothing for jobs already in flight. When you need a clean slate, <code class="language-plaintext highlighter-rouge">condor_rm</code> tears down old worker jobs (plenty of sites use <code class="language-plaintext highlighter-rouge">condor_rm -all</code> to wipe your queue); kill the factory on the submit side if it is still recycling the old scratch tree; install; submit again so what landed in the staging directory matches what you think you built.</p> <h2 id="turning-on-logs-people-can-open">Turning on logs people can open</h2> <p>Extra <code class="language-plaintext highlighter-rouge">fprintf</code> lines in <code class="language-plaintext highlighter-rouge">vine_worker</code> are not going to show up in your Python manager transcript; you still need worker-side debug. <code class="language-plaintext highlighter-rouge">-d all</code> is the heavy-handed default that turns the noise up across subsystems. Add <code class="language-plaintext highlighter-rouge">-o</code> with a path when stderr is useless because you will never SSH to that execute node (<a href="https://github.com/cooperative-computing-lab/cctools/blob/master/taskvine/src/worker/vine_worker_options.c">CLI reference</a>).</p> <p>Sanity check on the laptop first:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_worker <span class="nt">-d</span> all localhost 9123
</code></pre></div></div> <p>You want a wall of debug text right away; same flags later ride along inside Condor.</p> <p><code class="language-plaintext highlighter-rouge">vine_factory --debug-workers</code> appends <code class="language-plaintext highlighter-rouge">-d all -o worker.&lt;n&gt;.log</code> for each submission (the number is a factory-side counter, not Condor’s proc id). Those files hop back in the job sandbox when the run finishes because Condor uses <code class="language-plaintext highlighter-rouge">when_to_transfer_output = on_exit</code>, which is fine for an autopsy and useless if you wanted <code class="language-plaintext highlighter-rouge">tail -f</code> while the slot was still busy.</p> <p>For that you need a directory the execute node and your login node both mount. Hand the worker <code class="language-plaintext highlighter-rouge">-d all</code> and <code class="language-plaintext highlighter-rouge">-o /absolute/path/...</code> through <code class="language-plaintext highlighter-rouge">vine_factory</code> <code class="language-plaintext highlighter-rouge">-E,--extra-options</code>. Under the hood the Condor batch module writes a tiny <code class="language-plaintext highlighter-rouge">condor.sh</code> wrapper that does <code class="language-plaintext highlighter-rouge">eval "$@"</code> with the full command line as arguments (<a href="https://github.com/cooperative-computing-lab/cctools/blob/master/batch_job/src/batch_queue_condor.c"><code class="language-plaintext highlighter-rouge">batch_queue_condor.c</code></a>).</p> <p>Put something unique to each job in the filename. HTCondor’s <code class="language-plaintext highlighter-rouge">$(Cluster)</code> and <code class="language-plaintext highlighter-rouge">$(Process)</code> (or the <code class="language-plaintext highlighter-rouge">$(ClusterId)</code> / <code class="language-plaintext highlighter-rouge">$(ProcId)</code> spellings your site prefers) expand per proc; write them as <code class="language-plaintext highlighter-rouge">\$(Cluster)</code> and <code class="language-plaintext highlighter-rouge">\$(Process)</code> when an earlier shell would swallow the <code class="language-plaintext highlighter-rouge">$</code> before Condor sees them.</p> <p>Choose a parent dir <code class="language-plaintext highlighter-rouge">d</code> on the shared filesystem, maybe stamp <code class="language-plaintext highlighter-rouge">ts</code> when you kick off a factory, maybe add something like <code class="language-plaintext highlighter-rouge">ipid</code> if your wrapper exports it before <code class="language-plaintext highlighter-rouge">vine_worker</code> starts. The filename pattern is your own business as long as two workers never write the same path by accident.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">d</span><span class="o">=</span>/cephfs/group/myproject/vine-logs
<span class="nv">ts</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%Y%m%d%H%M%S<span class="si">)</span>
vine_factory <span class="nt">-T</span> condor <span class="nt">-M</span> myproject <span class="nt">-S</span> /scratch/<span class="nv">$USER</span>/vf <span class="se">\</span>
  <span class="nt">-E</span> <span class="s2">"-d all -o </span><span class="k">${</span><span class="nv">d</span><span class="k">}</span><span class="s2">/worker-</span><span class="k">${</span><span class="nv">ts</span><span class="k">}</span><span class="s2">-</span><span class="se">\$</span><span class="s2">(Cluster).</span><span class="se">\$</span><span class="s2">(Process).log"</span>
</code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">ts</code> in that example is just a label for one factory session; <code class="language-plaintext highlighter-rouge">\$(Cluster).\$(Process)</code> keeps different Condor procs from stepping on each other. If you already have something like <code class="language-plaintext highlighter-rouge">ipid</code> in the environment and you trust it to be unique per writer, <code class="language-plaintext highlighter-rouge">${d}/worker-${ts}-${ipid}-\$(Cluster).\$(Process).log</code> is the same idea with more belt and suspenders.</p> <p>Do not turn on <code class="language-plaintext highlighter-rouge">--debug-workers</code> and a fat custom <code class="language-plaintext highlighter-rouge">-E</code> at the same time unless you like reading everything twice.</p> <p>From Python the same string can ride through <a href="https://github.com/cooperative-computing-lab/cctools/blob/master/taskvine/src/bindings/python3/ndcctools/taskvine/manager.py"><code class="language-plaintext highlighter-rouge">vine.Factory.extra_options</code></a>, which maps to <code class="language-plaintext highlighter-rouge">vine_factory --extra-options</code>. There is still no <code class="language-plaintext highlighter-rouge">debug_workers</code> knob in the binding, so either stay explicit in <code class="language-plaintext highlighter-rouge">extra_options</code> or shell out to the CLI if you need the factory-only switch:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">ndcctools.taskvine</span> <span class="k">as</span> <span class="n">vine</span>

<span class="n">manager</span> <span class="o">=</span> <span class="n">vine</span><span class="p">.</span><span class="nc">Manager</span><span class="p">(</span><span class="mi">9123</span><span class="p">)</span>
<span class="n">factory</span> <span class="o">=</span> <span class="n">vine</span><span class="p">.</span><span class="nc">Factory</span><span class="p">(</span><span class="n">batch_type</span><span class="o">=</span><span class="sh">"</span><span class="s">condor</span><span class="sh">"</span><span class="p">,</span> <span class="n">manager</span><span class="o">=</span><span class="n">manager</span><span class="p">,</span> <span class="n">log_file</span><span class="o">=</span><span class="sh">"</span><span class="s">factory.log</span><span class="sh">"</span><span class="p">)</span>
<span class="n">factory</span><span class="p">.</span><span class="n">extra_options</span> <span class="o">=</span> <span class="sa">r</span><span class="sh">"</span><span class="s">-d all -o /cephfs/group/myproject/vine-logs/worker-\$(Cluster).\$(Process).log</span><span class="sh">"</span>
<span class="k">with</span> <span class="n">factory</span><span class="p">:</span>
    <span class="n">job</span> <span class="o">=</span> <span class="n">vine</span><span class="p">.</span><span class="nc">Task</span><span class="p">(</span><span class="sh">"</span><span class="s">echo heartbeat</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">manager</span><span class="p">.</span><span class="nf">submit</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
    <span class="n">manager</span><span class="p">.</span><span class="nf">wait</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">120</span><span class="p">)</span>
</code></pre></div></div> <p>When you pack up, skim the usual checklist: did the slot actually run the binary you staged, is <code class="language-plaintext highlighter-rouge">-d all</code> going somewhere you can read, and did you give each job a distinct path (Condor macros plus any label you added on purpose) so the logs never clobber each other.</p>]]></content><author><name>Cooperative Computing Lab</name></author><category term="technical-articles"/><category term="taskvine"/><category term="htcondor"/><category term="debugging"/><summary type="html"><![CDATA[On HTCondor, workers keep running the vine_worker binary that got staged at submit time, not the one you just installed into $PATH. Here is how to get a fresh build onto the nodes and how to point -d all output somewhere you can actually read.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/TaskVine-Insights.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/taskvine-insights-shipping-worker-builds-to-remote-nodes/TaskVine-Insights.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">TaskVine Insights: Submitting Workers to a Cluster</title><link href="https://ccl.cse.nd.edu/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/" rel="alternate" type="text/html" title="TaskVine Insights: Submitting Workers to a Cluster"/><published>2026-04-28T17:00:00+00:00</published><updated>2026-04-28T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/taskvine-insights-submitting-workers-to-a-cluster</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/"><![CDATA[<div class="row justify-content-sm-center"> <div class="col-sm-12"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/TaskVine-Insights-480.webp 480w,/assets/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/TaskVine-Insights-800.webp 800w,/assets/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/TaskVine-Insights-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/TaskVine-Insights.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" title="" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>A TaskVine application becomes useful on a cluster only after workers are placed where the compute capacity is. On a laptop, we may start a worker by hand:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_worker HOST PORT
</code></pre></div></div> <p>That is fine for a local test. It is not how we want to run a real workflow on a shared system. A cluster already has a resource manager, and TaskVine should use it. CCTools supports worker submission through several batch systems, including HTCondor, Slurm, and UGE. At the Notre Dame CRC compute cluster, the common path for our TaskVine runs is HTCondor, so this post uses Condor as the concrete example.</p> <p>There are two ways to think about cluster workers. <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> is the direct path. It submits a fixed number of workers and then gets out of the way. <code class="language-plaintext highlighter-rouge">vine_factory</code> is the managed path. It keeps watching the manager, estimates how many workers are needed, submits new workers when old ones disappear, and writes logs that are much easier to use during a postmortem.</p> <p>The <a href="https://cctools.readthedocs.io/en/latest/taskvine/">TaskVine User’s Manual</a> covers both tools. Here we focus on the HTCondor path and the Conda environment details that tend to matter in real runs.</p> <h2 id="the-runtime-shape">The runtime shape</h2> <p>A TaskVine manager listens on a host and port, or advertises itself under a project name through the catalog. Workers connect back to that manager. The batch system does not run your manager. It runs worker jobs that eventually execute <code class="language-plaintext highlighter-rouge">vine_worker</code> with the right connection arguments.</p> <p>That distinction explains the first command most users see in the manual:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_submit_workers <span class="nt">-T</span> condor MACHINENAME 9123 10
</code></pre></div></div> <p>This asks HTCondor to start ten workers. When those jobs begin to run, each worker calls back to <code class="language-plaintext highlighter-rouge">MACHINENAME:9123</code>. If your manager uses a project name, the command becomes:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_submit_workers <span class="nt">-T</span> condor <span class="nt">-M</span> myproject 10
</code></pre></div></div> <p>The project name form is usually easier once runs become repeatable, because workers can discover the manager from the catalog instead of hard coding a hostname and port.</p> <h2 id="direct-submission-with-vine_submit_workers">Direct submission with <code class="language-plaintext highlighter-rouge">vine_submit_workers</code></h2> <p><code class="language-plaintext highlighter-rouge">vine_submit_workers</code> is a shell script in CCTools. It supports Condor, Slurm, and UGE through <code class="language-plaintext highlighter-rouge">-T</code> or <code class="language-plaintext highlighter-rouge">--batch-type</code>. The Condor path is useful to understand because it shows what the tool is actually doing for us.</p> <p>For Condor, the script builds a small submission directory, usually <code class="language-plaintext highlighter-rouge">/tmp/$USER-workers</code>. It copies <code class="language-plaintext highlighter-rouge">vine_worker</code> into that directory. It also copies helper inputs such as <code class="language-plaintext highlighter-rouge">cctools_gpu_autodetect</code>. If <code class="language-plaintext highlighter-rouge">--poncho-env</code> is used, it also copies <code class="language-plaintext highlighter-rouge">poncho_package_run</code> and the packaged environment. Then it writes a <code class="language-plaintext highlighter-rouge">worker.sh</code> wrapper and a <code class="language-plaintext highlighter-rouge">condor_submit_file</code>.</p> <p>The generated Condor submit file uses <code class="language-plaintext highlighter-rouge">worker.sh</code> as the executable. It transfers the worker binary and all declared input files. It writes output to files named like <code class="language-plaintext highlighter-rouge">worker.$(CLUSTER).$(PROCESS).output</code>, error output to <code class="language-plaintext highlighter-rouge">worker.$(CLUSTER).$(PROCESS).error</code>, and Condor job events to <code class="language-plaintext highlighter-rouge">workers.log</code>. It also sets <code class="language-plaintext highlighter-rouge">getenv = true</code>, so the submitted job inherits the submit side environment unless you change the generated file or Condor policy rejects it.</p> <p>That automation is the main convenience. A practical HTCondor command looks like this:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_submit_workers <span class="se">\</span>
  <span class="nt">-T</span> condor <span class="se">\</span>
  <span class="nt">--cores</span> 4 <span class="se">\</span>
  <span class="nt">--memory</span> 8192 <span class="se">\</span>
  <span class="nt">--disk</span> 20000 <span class="se">\</span>
  <span class="nt">--poncho-env</span> dv5-env.tar.gz <span class="se">\</span>
  <span class="nt">-M</span> myproject <span class="se">\</span>
  20
</code></pre></div></div> <p>This submits twenty workers. Each worker advertises four cores, 8192 MB of memory, and 20000 MB of disk to TaskVine, and the Condor submit file requests matching resources from Condor.</p> <p>One common gotcha is argument order. The script parses options until it reaches the final positional arguments. Put the manager address, or the worker count for a project name, at the end. Also put <code class="language-plaintext highlighter-rouge">-T condor</code> before Condor specific options such as <code class="language-plaintext highlighter-rouge">--requirements</code>, because the script only knows how to parse those options after the batch system has been selected. The two accepted shapes are:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_submit_workers <span class="o">[</span>options] HOST PORT NUM_WORKERS
vine_submit_workers <span class="o">[</span>options] <span class="nt">-M</span> PROJECT NUM_WORKERS
</code></pre></div></div> <p>Another easy mistake is <code class="language-plaintext highlighter-rouge">localhost</code>. The script explicitly rejects <code class="language-plaintext highlighter-rouge">localhost</code> for Condor workers. A worker running on a remote execute node would interpret <code class="language-plaintext highlighter-rouge">localhost</code> as the execute node itself, not the manager. Use a routable hostname or a project name.</p> <p>Finally, <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> submits a fixed number of jobs and exits. In the Condor path, the script writes <code class="language-plaintext highlighter-rouge">queue NUM_WORKERS</code>, runs <code class="language-plaintext highlighter-rouge">condor_submit</code>, and does not leave behind a monitor. If a worker exits because of a worker side bug, a segmentation fault, preemption, a node problem, or a random distributed systems failure, the submit command is no longer around to replace it. The manager can retry interrupted tasks when another worker is available, but the worker pool itself may silently shrink. You need to notice that through <code class="language-plaintext highlighter-rouge">condor_q</code>, TaskVine status, manager logs, or slow progress, and then resubmit workers yourself.</p> <h2 id="common-vine_submit_workers-options">Common <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> options</h2> <p>Do not treat <code class="language-plaintext highlighter-rouge">vine_submit_workers --help</code> as a checklist. Most runs only need a small part of it.</p> <p>Where to submit:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-T, --batch-type condor|slurm|uge</code>: choose the batch system. On Notre Dame CRC, we usually use <code class="language-plaintext highlighter-rouge">condor</code>.</li> <li><code class="language-plaintext highlighter-rouge">HOST PORT NUM_WORKERS</code>: connect workers directly to a manager address.</li> <li><code class="language-plaintext highlighter-rouge">-M, --manager-name PROJECT NUM_WORKERS</code>: connect workers to a manager advertised through the catalog.</li> </ul> <p>How many resources each worker should have:</p> <ul> <li><code class="language-plaintext highlighter-rouge">--cores N</code>: request this many cores and report the same number to TaskVine.</li> <li><code class="language-plaintext highlighter-rouge">--memory MB</code>: request this much memory and report it to TaskVine.</li> <li><code class="language-plaintext highlighter-rouge">--disk MB</code>: request this much disk and report it to TaskVine.</li> </ul> <p>How workers run:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-t, --timeout SECONDS</code>: let idle workers exit after this time. The default is 900 seconds.</li> <li><code class="language-plaintext highlighter-rouge">--scratch-dir PATH</code>: choose where the local submit directory is created.</li> <li><code class="language-plaintext highlighter-rouge">--poncho-env FILE.tar.gz</code>: run the worker inside a packaged Poncho environment.</li> <li><code class="language-plaintext highlighter-rouge">--dry-run</code>: print the generated worker script and Condor submit file instead of submitting. Use this before submitting if the command has changed.</li> </ul> <p>Condor placement:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-r, --requirements EXPR</code>: add a Condor requirements expression.</li> </ul> <p>The remaining flags are for less common site policies and advanced worker behavior. Leave them out of the first version of a run. When one of those cases comes up, check the <a href="https://cctools.readthedocs.io/en/latest/taskvine/">TaskVine manual</a> and the <a href="https://github.com/cooperative-computing-lab/cctools/blob/master/taskvine/src/tools/vine_submit_workers"><code class="language-plaintext highlighter-rouge">vine_submit_workers</code> source</a>.</p> <h2 id="why-a-factory-is-usually-better">Why a factory is usually better</h2> <p>Direct submission is convenient when the run is small, short, or interactive. For a serious workflow, the better default is <code class="language-plaintext highlighter-rouge">vine_factory</code>.</p> <p>The factory is a long running process. It periodically asks the manager, or the catalog, what work exists and how many workers are connected. It then compares the desired worker count with the workers it has already submitted. If it needs more workers, it submits them through the selected batch system. If workers exit, it observes those exits, removes them from its internal job table, and can submit replacements on the next cycle.</p> <p>That behavior matters on shared clusters. HTCondor can preempt jobs. Nodes can fail. Worker processes can crash. A manager can keep retrying tasks, but it cannot create new batch jobs by itself. The factory fills that gap.</p> <p>A normal Condor factory command looks like this:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_factory <span class="se">\</span>
  <span class="nt">-T</span> condor <span class="se">\</span>
  <span class="nt">--min-workers</span> 2 <span class="se">\</span>
  <span class="nt">--max-workers</span> 50 <span class="se">\</span>
  <span class="nt">--workers-per-cycle</span> 10 <span class="se">\</span>
  <span class="nt">--cores</span> 4 <span class="se">\</span>
  <span class="nt">--memory</span> 8192 <span class="se">\</span>
  <span class="nt">--disk</span> 20000 <span class="se">\</span>
  <span class="nt">--poncho-env</span> dv5-env.tar.gz <span class="se">\</span>
  <span class="nt">--manager-name</span> myproject
</code></pre></div></div> <p>This keeps at least two workers and at most fifty workers available for managers named <code class="language-plaintext highlighter-rouge">myproject</code>. The factory checks the manager periodically, estimates worker demand from waiting and running tasks, caps the target at <code class="language-plaintext highlighter-rouge">--max-workers</code>, raises it to <code class="language-plaintext highlighter-rouge">--min-workers</code> if needed, and submits at most <code class="language-plaintext highlighter-rouge">--workers-per-cycle</code> new jobs in a single cycle.</p> <p>The implementation keeps an internal table of submitted batch job IDs. Each submitted worker runs a generated <code class="language-plaintext highlighter-rouge">vine_worker</code> command with the manager target, timeout, and requested resource shape. The factory then calls the batch queue layer to submit that job. Later, it waits for completed jobs with a short timeout. When a known worker job exits, the factory decrements its submitted count. That is what lets it replace lost workers without you watching <code class="language-plaintext highlighter-rouge">condor_q</code> all afternoon.</p> <p>The logs are also useful. <code class="language-plaintext highlighter-rouge">vine_factory -d vine -o factory.log</code> gives you a persistent record of what the factory thought the manager needed, how many jobs it submitted, and which jobs exited. <code class="language-plaintext highlighter-rouge">--debug-workers</code> adds per worker debug logs in the factory scratch directory. When a run fails at scale, this is much easier to debug than a one time submit command whose only durable state is in Condor.</p> <h2 id="common-vine_factory-options">Common <code class="language-plaintext highlighter-rouge">vine_factory</code> options</h2> <p>The factory has many knobs because it is a controller, not only a submit command. For normal Condor use, start with these.</p> <p>Where to submit and which manager to serve:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-T, --batch-type TYPE</code>: required batch system type. Use <code class="language-plaintext highlighter-rouge">condor</code> for HTCondor.</li> <li><code class="language-plaintext highlighter-rouge">-M, -N, --manager-name PROJECT</code>: serve managers matching a project name or regular expression.</li> <li><code class="language-plaintext highlighter-rouge">HOST PORT</code>: connect directly to one manager instead of using the catalog.</li> <li><code class="language-plaintext highlighter-rouge">-C, --config-file FILE</code>: read options from a JSON config file. The factory re-reads this file periodically.</li> </ul> <p>How many workers to keep around:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-w, --min-workers N</code>: keep at least this many workers.</li> <li><code class="language-plaintext highlighter-rouge">-W, --max-workers N</code>: never ask for more than this many workers.</li> <li><code class="language-plaintext highlighter-rouge">--workers-per-cycle N</code>: limit how many new workers the factory submits in one cycle.</li> <li><code class="language-plaintext highlighter-rouge">-t, --timeout SECONDS</code>: idle timeout passed to each worker.</li> <li><code class="language-plaintext highlighter-rouge">--factory-period SECONDS</code>: how often the factory re-evaluates demand.</li> </ul> <p>Worker resources:</p> <ul> <li><code class="language-plaintext highlighter-rouge">--cores N</code>: request and advertise cores per worker.</li> <li><code class="language-plaintext highlighter-rouge">--memory MB</code>: request and advertise memory per worker.</li> <li><code class="language-plaintext highlighter-rouge">--disk MB</code>: request and advertise disk per worker.</li> <li><code class="language-plaintext highlighter-rouge">--gpus N</code>: request and advertise GPUs per worker.</li> </ul> <p>Worker environment and logs:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-S, --scratch-dir PATH</code>: choose the factory scratch directory.</li> <li><code class="language-plaintext highlighter-rouge">-d, --debug SUBSYSTEM</code>: enable factory debugging.</li> <li><code class="language-plaintext highlighter-rouge">-o, --debug-file FILE</code>: write factory debug output to a file.</li> <li><code class="language-plaintext highlighter-rouge">--debug-workers</code>: create worker side debug logs.</li> <li><code class="language-plaintext highlighter-rouge">--poncho-env FILE.tar.gz</code>: run each worker in a Poncho package.</li> </ul> <p>Condor placement:</p> <ul> <li><code class="language-plaintext highlighter-rouge">--condor-requirements EXPR</code>: add Condor requirements. Multiple uses are combined.</li> </ul> <p>The remaining factory options are for less common site policies and advanced worker setups. Leave them out of the first version of a run unless your cluster setup requires them. For the complete set, use the <a href="https://cctools.readthedocs.io/en/latest/taskvine/">TaskVine manual</a> and the <a href="https://github.com/cooperative-computing-lab/cctools/blob/master/batch_job/src/vine_factory.c"><code class="language-plaintext highlighter-rouge">vine_factory</code> source</a>.</p> <p>A config file is often cleaner than a long command:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"manager-name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"myproject"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"min-workers"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
  </span><span class="nl">"max-workers"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span><span class="w">
  </span><span class="nl">"workers-per-cycle"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w">
  </span><span class="nl">"factory-period"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="p">,</span><span class="w">
  </span><span class="nl">"cores"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
  </span><span class="nl">"memory"</span><span class="p">:</span><span class="w"> </span><span class="mi">8192</span><span class="p">,</span><span class="w">
  </span><span class="nl">"disk"</span><span class="p">:</span><span class="w"> </span><span class="mi">20000</span><span class="p">,</span><span class="w">
  </span><span class="nl">"condor-requirements"</span><span class="p">:</span><span class="w"> </span><span class="s2">"TARGET.OpSysAndVer == </span><span class="se">\"</span><span class="s2">AlmaLinux9</span><span class="se">\"</span><span class="s2">"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>Then start the factory with:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_factory <span class="nt">-T</span> condor <span class="nt">-C</span> factory.json <span class="nt">--poncho-env</span> dv5-env.tar.gz
</code></pre></div></div> <p>The config file is re-read while the factory runs, so you can raise <code class="language-plaintext highlighter-rouge">max-workers</code>, lower <code class="language-plaintext highlighter-rouge">min-workers</code>, or adjust resource settings without restarting the controller.</p> <h2 id="a-small-wrapper-for-regular-crc-runs">A small wrapper for regular CRC runs</h2> <p>After a command becomes part of a weekly workflow, it is worth wrapping the repeated parts. In our day to day runs, we usually know the manager project name, the desired number of workers, the per worker resource shape, the scratch location, and the Poncho package. A thin shell wrapper makes that intent easier to read than a long <code class="language-plaintext highlighter-rouge">vine_factory</code> command.</p> <p>Here is a cleaned up version of the pattern:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>

<span class="nv">MANAGER_NAME</span><span class="o">=</span>dagvine-manager
<span class="nv">N_WORKERS</span><span class="o">=</span>40
<span class="nv">CORES</span><span class="o">=</span>16
<span class="nv">MEMORY_GB</span><span class="o">=</span>12
<span class="nv">DISK_GB</span><span class="o">=</span>100
<span class="nv">PONCHO_ENV</span><span class="o">=</span>dagvine-env.tar.gz
<span class="nv">SCRATCH_DIR</span><span class="o">=</span>/scratch365/<span class="nv">$USER</span>/factory_dv5
<span class="nv">CONDOR_REQUIREMENTS</span><span class="o">=</span><span class="s1">'((has_vast))'</span>

<span class="k">while</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-gt</span> 0 <span class="o">]</span>
<span class="k">do
    if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"-M"</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--manager-name"</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">MANAGER_NAME</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
        <span class="nb">shift </span>2
    <span class="k">elif</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--workers"</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">N_WORKERS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
        <span class="nb">shift </span>2
    <span class="k">elif</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--cores"</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">CORES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
        <span class="nb">shift </span>2
    <span class="k">elif</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--memory"</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">MEMORY_GB</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
        <span class="nb">shift </span>2
    <span class="k">elif</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--disk"</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">DISK_GB</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
        <span class="nb">shift </span>2
    <span class="k">elif</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--poncho-env"</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">PONCHO_ENV</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
        <span class="nb">shift </span>2
    <span class="k">else
        </span><span class="nb">echo</span> <span class="s2">"unknown option: </span><span class="nv">$1</span><span class="s2">"</span> <span class="o">&gt;</span>&amp;2
        <span class="nb">exit </span>2
    <span class="k">fi
done

</span><span class="nv">MEMORY_MB</span><span class="o">=</span><span class="k">$((</span>MEMORY_GB <span class="o">*</span> <span class="m">1024</span><span class="k">))</span>
<span class="nv">DISK_MB</span><span class="o">=</span><span class="k">$((</span>DISK_GB <span class="o">*</span> <span class="m">1024</span><span class="k">))</span>

vine_factory <span class="se">\</span>
    <span class="nt">-T</span> condor <span class="se">\</span>
    <span class="nt">--scratch-dir</span> <span class="s2">"</span><span class="nv">$SCRATCH_DIR</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--poncho-env</span> <span class="s2">"</span><span class="nv">$PONCHO_ENV</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--condor-requirements</span> <span class="s2">"</span><span class="nv">$CONDOR_REQUIREMENTS</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--manager-name</span> <span class="s2">"</span><span class="nv">$MANAGER_NAME</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--min-workers</span> <span class="s2">"</span><span class="nv">$N_WORKERS</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--max-workers</span> <span class="s2">"</span><span class="nv">$N_WORKERS</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--workers-per-cycle</span> <span class="s2">"</span><span class="nv">$N_WORKERS</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--cores</span> <span class="s2">"</span><span class="nv">$CORES</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--memory</span> <span class="s2">"</span><span class="nv">$MEMORY_MB</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--disk</span> <span class="s2">"</span><span class="nv">$DISK_MB</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--timeout</span> 36000
</code></pre></div></div> <p>The wrapper intentionally sets <code class="language-plaintext highlighter-rouge">min-workers</code>, <code class="language-plaintext highlighter-rouge">max-workers</code>, and <code class="language-plaintext highlighter-rouge">workers-per-cycle</code> to the same value. That makes the command behave like a fixed size pool, but with the factory still watching for worker exits and replacing lost jobs. The <code class="language-plaintext highlighter-rouge">--timeout 36000</code> setting keeps workers around for long runs. The <code class="language-plaintext highlighter-rouge">--condor-requirements '((has_vast))'</code> line is site specific. Keep it if your workflow needs that Condor attribute, and replace it with the requirement expression that matches your cluster policy otherwise.</p> <p>The script also accepts resource values in GB for memory and disk, then converts them to MB because <code class="language-plaintext highlighter-rouge">vine_factory --memory</code> and <code class="language-plaintext highlighter-rouge">--disk</code> expect megabytes. That small conversion removes a common source of mistakes when we are switching between Condor habits, shell scripts, and TaskVine options.</p> <h2 id="conda-environments-and-poncho">Conda environments and Poncho</h2> <p>Mismatched Conda environments create failures that look unrelated to worker submission. The worker binary, Python packages, shared libraries, and task imports should agree with the manager side assumptions. Start the manager, <code class="language-plaintext highlighter-rouge">vine_submit_workers</code>, and <code class="language-plaintext highlighter-rouge">vine_factory</code> from the same intended Conda environment whenever possible.</p> <p>For Python heavy workflows, package that environment and attach it to every worker. The workflow is small:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda activate dv5-env
poncho_package_create <span class="s2">"</span><span class="k">${</span><span class="nv">CONDA_PREFIX</span><span class="k">}</span><span class="s2">"</span> dv5-env.tar.gz
</code></pre></div></div> <p>Then pass the tarball to the submitter:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_submit_workers <span class="nt">-T</span> condor <span class="nt">--poncho-env</span> dv5-env.tar.gz <span class="nt">-M</span> myproject 20
</code></pre></div></div> <p>or to the factory:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vine_factory <span class="nt">-T</span> condor <span class="nt">--poncho-env</span> dv5-env.tar.gz <span class="nt">--min-workers</span> 2 <span class="nt">--max-workers</span> 50 <span class="nt">-M</span> myproject
</code></pre></div></div> <p>The details are handled for you. <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> ships <code class="language-plaintext highlighter-rouge">poncho_package_run</code> and the tarball as Condor input files, then runs <code class="language-plaintext highlighter-rouge">./poncho_package_run -e ENV.tar.gz -- ./vine_worker ...</code>. <code class="language-plaintext highlighter-rouge">vine_factory</code> implements <code class="language-plaintext highlighter-rouge">--poncho-env</code> as a wrapper around the worker command and adds both the wrapper tool and package as worker inputs. In both cases, the worker starts inside the packaged environment before it connects to the manager.</p> <h2 id="htcondor-operations-we-actually-use">HTCondor operations we actually use</h2> <p>Once workers are submitted, Condor is still the place to inspect and clean up batch jobs.</p> <p>Check your queued and running worker jobs:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>condor_q
</code></pre></div></div> <p>Remove workers when you want to stop cleanly:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>condor_rm <span class="nt">-all</span>
</code></pre></div></div> <p>On a shared system, be careful with <code class="language-plaintext highlighter-rouge">-all</code>. It removes your Condor jobs, and that is normally what we want when a TaskVine experiment needs a clean restart. After removal, also check for leftover local processes on the submit host:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux | <span class="nb">grep </span>vine_worker
ps aux | <span class="nb">grep </span>vine_factory
</code></pre></div></div> <p>This matters because a stale factory can quietly submit new workers after you think the old run is gone. A stale worker or wrapper process can also leave the environment in a state that does not match the next manager run. Clean up before changing Conda environments, project names, worker resources, or package tarballs.</p> <p>Condor also lets you enter a worker sandbox:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>condor_ssh_to_job JOBID
</code></pre></div></div> <p>This is useful when a worker behaves differently on the execute node than it does on the submit node. You can inspect transferred files, the unpacked Poncho environment, logs, temporary outputs, and the actual command environment. For a worker crash, combine <code class="language-plaintext highlighter-rouge">condor_ssh_to_job</code>, <code class="language-plaintext highlighter-rouge">worker.$CLUSTER.$PROCESS.error</code>, factory logs, and manager logs before changing code.</p> <h2 id="which-tool-should-you-use">Which tool should you use</h2> <p>Use <code class="language-plaintext highlighter-rouge">vine_submit_workers</code> at the beginning. It is a good way to run a very small workflow, or a simple bag of tasks, and check that the basics are correct: the manager is reachable, workers can start on Condor, the Poncho package works, and the requested resources make sense.</p> <p>After that first check, switch to <code class="language-plaintext highlighter-rouge">vine_factory</code> and make it the habit. Normal cluster work should not depend on manually noticing that workers disappeared. The factory has a control loop. It notices exited worker jobs, keeps the pool between <code class="language-plaintext highlighter-rouge">min-workers</code> and <code class="language-plaintext highlighter-rouge">max-workers</code>, throttles new submissions with <code class="language-plaintext highlighter-rouge">workers-per-cycle</code>, adapts to manager demand, and leaves behind logs that explain its decisions.</p> <p>The manager still owns task scheduling and retries. Condor still owns placement and preemption. The factory sits between them and keeps the worker supply healthy. A good workflow is to validate with <code class="language-plaintext highlighter-rouge">vine_submit_workers</code>, then run real workloads with <code class="language-plaintext highlighter-rouge">vine_factory</code>.</p>]]></content><author><name>Cooperative Computing Lab</name></author><category term="technical-articles"/><category term="taskvine"/><category term="htcondor"/><summary type="html"><![CDATA[TaskVine workers can be submitted directly to batch systems or managed dynamically with vine_factory, with HTCondor details for worker resources, environment packaging, cleanup, and debugging.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ccl.cse.nd.edu/assets/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/TaskVine-Insights.png"/><media:content medium="image" url="https://ccl.cse.nd.edu/assets/blog/2026/taskvine-insights-submitting-workers-to-a-cluster/TaskVine-Insights.png" xmlns:media="http://search.yahoo.com/mrss/"/></entry><entry><title type="html">Congratulations to Colin Thomas on Passing His Candidacy Exam</title><link href="https://ccl.cse.nd.edu/blog/2026/congratulations-to-colin-on-passing-his-candidacy-exam/" rel="alternate" type="text/html" title="Congratulations to Colin Thomas on Passing His Candidacy Exam"/><published>2026-04-21T17:00:00+00:00</published><updated>2026-04-21T17:00:00+00:00</updated><id>https://ccl.cse.nd.edu/blog/2026/congratulations-to-colin-on-passing-his-candidacy-exam</id><content type="html" xml:base="https://ccl.cse.nd.edu/blog/2026/congratulations-to-colin-on-passing-his-candidacy-exam/"><![CDATA[<p>Colin Thomas passed his Ph.D. candidacy exam this April. We are thrilled for him, and congratulations, Colin. A lot of reading, writing, and committee prep went into getting here, and it paid off.</p> <p>Colin is a Ph.D. student in Computer Science and Engineering at Notre Dame and works in our group with Prof. Douglas Thain. His research interests are distributed systems and scientific workflows on clusters. Much of his work so far has centered on Parsl and TaskVine, on how scheduling and data placement behave when workflows mix a parallel filesystem with node-local disks, and on keeping related tasks and their intermediate files together instead of shuffling everything through shared storage. With candidacy behind him, we are looking forward to the next stretch of the Ph.D. and what he will build and publish from here.</p> <p>Nice work on candidacy, Colin.</p>]]></content><author><name>Cooperative Computing Lab</name></author><category term="news"/><category term="news"/><summary type="html"><![CDATA[Congratulations to Colin Thomas on his Ph.D. candidacy in April 2026.]]></summary></entry></feed>