Managing Python packages and environments on DAIC.
---
@@ -51,20 +51,22 @@ This tutorial covers all four, starting with UV (recommended for most users).
---
## Part 1: UV - The modern Python workflow
## Part 1: UV - project-based environments
[UV](https://docs.astral.sh/uv/) is a fast Python package manager written in Rust. It replaces pip, virtualenv, and pip-tools with a single tool that's 10-100x faster.
[UV](https://docs.astral.sh/uv/) is a fast Python package manager written in Rust. It replaces pip, virtualenv, and pip-tools with a single tool.
### Why UV?
-**Speed**: Installs packages in seconds, not minutes
-**Speed**: Parallel downloads and a shared package cache keep installs short
-**Lockfiles**: `uv.lock` records exact versions for reproducibility
-**Project-based**: Each project has its own isolated environment
First, ensure your shell is configured for DAIC storage (see [Shell Setup](/quickstart/shell-setup/)):
{{% alert title="Set up your shell first" color="warning" %}}
Complete [Shell Setup](/quickstart/shell-setup/) before installing any of the tools in this tutorial. Without `.daicrc`, the installers and package caches write to your 5 MB cluster home and fill it.
{{% /alert %}}
```shell-session
$curl -LsSf https://astral.sh/uv/install.sh | sh
@@ -80,7 +82,7 @@ Verify the installation:
```shell-session
$uv --version
uv 0.6.x
uv 0.x.y
```
### Creating a project
@@ -89,17 +91,21 @@ Navigate to your project storage and create a new project:
```shell-session
$cd /tudelft.net/staff-umbrella/<project>
$uv init ml-experiment
$uv init --no-packageml-experiment
$cd ml-experiment
$ls
README.md hello.py pyproject.toml
README.md main.py pyproject.toml
```
The `--no-package` option creates a project that holds scripts rather than an installable Python package, which is what most research code needs.
UV created three files:
-`pyproject.toml`: Project metadata and dependencies
-`hello.py`: A sample Python file
-`main.py`: A sample Python file
-`README.md`: Project documentation
It also initialised a Git repository and added the hidden files `.gitignore` and `.python-version` (`ls -a` shows them).
Look at the project configuration:
```shell-session
@@ -210,7 +216,6 @@ Create a batch script that uses your UV project:
#SBATCH --output=train_%j.out
module purge
module load 2025/gpu cuda/12.9
cd /tudelft.net/staff-umbrella/<project>/ml-experiment
@@ -219,6 +224,8 @@ srun uv run python train.py
echo"Finished at $(date)"
```
The script loads no CUDA module. PyTorch packages from PyPI and conda-forge bundle the CUDA runtime libraries they need, so the only requirement on the node is a sufficiently recent GPU driver (see [CUDA version mismatch](#cuda-version-mismatch)). The same applies to the Pixi and Micromamba jobs below.
Submit it:
```shell-session
@@ -226,39 +233,49 @@ $ sbatch train_job.sh
Submitted batch job 12345
```
### Installing PyTorch with CUDA
### Choosing a PyTorch CUDA build
For GPU support, specify the PyTorch index:
On Linux, `uv add torch` installs a CUDA-enabled build from PyPI, so GPU support needs no extra steps. You only need the PyTorch package index when you want a build for a specific CUDA version, for example because the default build is newer than the GPU driver supports.
`explicit = true` restricts the index to the packages listed under `[tool.uv.sources]`. Without it, UV would also look up every other dependency on the PyTorch index, which carries outdated copies of common packages such as NumPy.
### Installing CLI tools
UV can install command-line tools globally (independent of projects):
```shell-session
$uv tool install ruff
$uv tool install black
$uv tool install jupyter
$uv tool install jupyterlab
$ruff --version
ruff 0.9.1
$uv tool list
black v24.10.0
jupyter v1.0.0
jupyterlab v4.3.4
ruff v0.9.1
```
Install `jupyterlab`, not `jupyter`: the `jupyter` package is a metapackage without executables of its own, and `uv tool install` rejects it.
### Syncing on another machine
When you clone a project with UV, restore the exact environment:
@@ -282,8 +299,9 @@ The lockfile ensures you get the exact same versions.
{{% alert title="Check your work" color="info" %}}
The `-c` options set the channels the project installs from. [Bioconda](https://bioconda.github.io/) provides bioinformatics software that conda-forge does not carry; omit it if you do not need it.
### Adding packages
Add packages from conda-forge:
Add packages from the configured channels:
```shell-session
$pixi add python=3.11 numpy pandas
$pixi add biopython samtools # packages not on PyPI
$pixi add biopython
$pixi add samtools # command-line tool from bioconda, not available on PyPI
```
Check the configuration:
```shell-session
$cat pixi.toml
[project]
[workspace]
name = "bioinformatics-project"
channels = ["conda-forge"]
channels = ["conda-forge", "bioconda"]
platforms = ["linux-64"]
[dependencies]
python = "3.11.*"
numpy = "*"
pandas = "*"
biopython = "*"
samtools = "*"
numpy = ">=2.2.1,<3"
pandas = ">=2.2.3,<3"
biopython = ">=1.84,<2"
samtools = ">=1.21,<2"
```
### Running commands
@@ -383,7 +404,6 @@ $
#SBATCH --output=analysis_%j.out
module purge
module load 2025/gpu cuda/12.9
cd /tudelft.net/staff-umbrella/<project>/bioinformatics-project
@@ -427,21 +447,25 @@ If you see an import error, check that biopython was added to `pixi.toml`.
$"${SHELL}" <(curl -L micro.mamba.pm/install.sh)
```
When prompted for the installation location, use project storage:
The installer asks where to put the binary and the environments. Keep both out of the cluster home:
These match what `.daicrc` configures: `~/linuxhome/.local/bin` is on your `PATH`, and `MAMBA_ROOT_PREFIX` points to `~/linuxhome/.mamba`, so named environments are stored under `~/linuxhome/.mamba/envs`.
To keep an environment in project storage instead, for example to share it with your group, create it by path with `-p` rather than by name:
```shell-session
$micromamba config set env_path /tudelft.net/staff-umbrella/<project>/micromamba/envs
If you need a specific CUDA build of PyTorch, first add the index configuration from [Choosing a PyTorch CUDA build](#choosing-a-pytorch-cuda-build) to `pyproject.toml`.
The login node is not meant for computation, so run the quick test in a short interactive allocation (see [Slurm Basics](/tutorials/slurm/#interactive-jobs-for-testing)). Submit from the project directory: the `--output` path in the job script is relative to the directory you submit from, and the job fails without any output if `outputs/` does not exist there.
@@ -747,15 +774,18 @@ Job finished: Fri Mar 20 15:30:00 CET 2026
## Troubleshooting
### "No space left on device"
### "Disk quota exceeded"
Your cluster home is full (5 MB limit), usually because a tool wrote its cache there.
Your home directory is full (5 MB limit).
**Solution**: Install `.daicrc` as described in [Shell Setup](/quickstart/shell-setup/); it redirects the caches of UV, Pixi, conda, pip and others to `~/linuxhome`. To free a home that is already full, see [Redirecting caches out of the cluster home](/docs/storage/storage/#redirecting-caches-out-of-the-cluster-home).
**Solution**: Move caches to project storage. Add to `~/.bashrc`:
### "Failed to hardlink files; falling back to full copy"
UV links packages from its cache into `.venv/`. The cache is in `~/linuxhome` and your project is in project storage; these are different filesystems, so linking is not possible and UV copies the files instead. The warning is harmless. Silence it by adding this to `~/.bashrc`:
@@ -776,21 +806,19 @@ srun uv run python src/train.py
### CUDA version mismatch
PyTorch can't find CUDA or wrong version.
`torch.cuda.is_available()` returns `False` on a GPU node, or PyTorch reports that the driver is too old.
**Solution**: Match PyTorch CUDA version to the host driver. Check driver version:
**Cause**: The PyTorch build was compiled for a newer CUDA version than the node's GPU driver supports. A CUDA module does not change this: PyTorch uses its bundled CUDA runtime, and the driver decides which runtime versions work.
```shell-session
$nvidia-smi | grep"Driver Version"
Driver Version: 550.54.15 CUDA Version: 12.4
```
Then install matching PyTorch:
**Solution**: Check the driver on a compute node. The login node has no GPU, so run this inside a GPU job or interactive session:
```shell-session
$uv add torch --index https://download.pytorch.org/whl/cu124 # for CUDA 12.4
$srun nvidia-smi | grep"Driver Version"
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
```
"CUDA Version" is the newest CUDA runtime the driver supports. Select a PyTorch build for that version or an older one, as described in [Choosing a PyTorch CUDA build](#choosing-a-pytorch-cuda-build)(`cu124` for CUDA 12.4).
### Slow package installation
Package resolution takes forever.
@@ -855,14 +883,14 @@ You've learned to manage Python environments on DAIC:
1.**Use UV for most projects** - it's fast and handles lockfiles automatically
2.**Store everything in project storage** - never in `/home` (5 MB limit)
3.**Commit lockfiles** - `uv.lock` or `pixi.lock` for reproducibility
4.**Test locally before submitting** - catch errors early
5.**Match CUDA versions** - module CUDA version must match PyTorch build
4.**Test in a short interactive session before submitting** - catch errors early
5.**Match the PyTorch build to the GPU driver** - the build's CUDA version must not be newer than the one `nvidia-smi` reports