Commit 0414d69f authored by Sören Wacker's avatar Sören Wacker
Browse files

Fix Python tutorial examples that fail and align cache and CUDA advice with .daicrc

parent 91811871
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -49,14 +49,14 @@ $ uv run python train.py

### Install CLI tools

Install Python tools globally (ruff, black, jupyter, etc.):
Install Python tools globally (ruff, JupyterLab, etc.):

```shell-session
$ uv tool install ruff
$ uv tool install jupyter
$ uv tool install jupyterlab

$ ruff --version
$ jupyter lab
$ jupyter-lab --version
```

List installed tools:
+96 −68
Original line number Diff line number Diff line
---
title: "Python environments"
weight: 5
weight: 3
description: >
  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
- **No activation needed**: `uv run` handles everything

### Installing UV

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-package ml-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.

```shell-session
$ uv add torch --index https://download.pytorch.org/whl/cu124
Declare the index in `pyproject.toml` and bind only the PyTorch packages to it:

```toml
[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
explicit = true

[tool.uv.sources]
torch = { index = "pytorch-cu124" }
torchvision = { index = "pytorch-cu124" }
```

Or add it to `pyproject.toml`:
Then add the packages as usual:

```toml
[tool.uv]
index-url = "https://download.pytorch.org/whl/cu124"
```shell-session
$ uv add torch torchvision
```

`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" %}}
```shell-session
$ ls data-analysis/
README.md  hello.py  pyproject.toml  uv.lock  .venv
$ cd data-analysis
$ ls -A
.git  .gitignore  .python-version  .venv  README.md  main.py  pyproject.toml  uv.lock

$ uv run python -c "import sklearn; print(sklearn.__version__)"
1.6.0
@@ -314,36 +332,39 @@ pixi 0.40.x

```shell-session
$ cd /tudelft.net/staff-umbrella/<project>
$ pixi init bioinformatics-project
$ pixi init -c conda-forge -c bioconda bioinformatics-project
$ cd bioinformatics-project
$ ls
pixi.toml
```

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:
```
Micromamba binary folder: /tudelft.net/staff-umbrella/<project>/micromamba/bin
Micromamba binary folder? [~/.local/bin] ~/linuxhome/.local/bin
Prefix location? [~/micromamba] ~/linuxhome/.mamba
```

Configure the environment prefix:
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
$ micromamba create -p /tudelft.net/staff-umbrella/<project>/envs/pytorch-env python=3.11
$ micromamba activate /tudelft.net/staff-umbrella/<project>/envs/pytorch-env
```

### Creating environments

```shell-session
$ micromamba create -n pytorch-env python=3.11 pytorch numpy -c conda-forge -c pytorch
$ micromamba create -n pytorch-env python=3.11 pytorch numpy -c conda-forge
$ micromamba activate pytorch-env

(pytorch-env) $ python -c "import torch; print(torch.__version__)"
@@ -453,7 +477,7 @@ $ micromamba activate pytorch-env
```shell-session
$ micromamba env list
  Name        Active  Path
  pytorch-env    *    /tudelft.net/.../micromamba/envs/pytorch-env
  pytorch-env    *    /home/netid01/linuxhome/.mamba/envs/pytorch-env

$ micromamba deactivate
```
@@ -478,7 +502,6 @@ $ micromamba activate pytorch-env
#SBATCH --output=train_%j.out

module purge
module load 2025/gpu cuda/12.9

# Initialize micromamba for this shell
eval "$(micromamba shell hook --shell bash)"
@@ -594,7 +617,7 @@ ml-project/

```shell-session
$ cd /tudelft.net/staff-umbrella/<project>
$ uv init ml-project
$ uv init --no-package ml-project
$ cd ml-project
$ mkdir -p src configs jobs outputs
```
@@ -602,10 +625,11 @@ $ mkdir -p src configs jobs outputs
### Add dependencies

```shell-session
$ uv add torch torchvision --index https://download.pytorch.org/whl/cu124
$ uv add numpy pandas matplotlib pyyaml tqdm
$ uv add torch torchvision numpy pandas matplotlib pyyaml tqdm
```

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`.

### Training script

```shell-session
@@ -687,7 +711,6 @@ $ cat > jobs/train.sh << 'EOF'

# Clean environment
module purge
module load 2025/gpu cuda/12.9

# Navigate to project
cd /tudelft.net/staff-umbrella/<project>/ml-project
@@ -707,13 +730,17 @@ echo "=========================================="
EOF
```

### Test locally, then submit
### Test interactively, then submit

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.

```shell-session
# Quick test on login node (CPU only)
$ uv run python src/train.py
# Quick test on a compute node
$ salloc --account=<your-account> --partition=all --time=0:15:00 --cpus-per-task=4 --mem=8G --gres=gpu:1
$ srun uv run python src/train.py
$ exit

# Submit to cluster for GPU training
# Submit the full job
$ sbatch jobs/train.sh
Submitted batch job 12345

@@ -734,7 +761,7 @@ After the job completes:
$ ls outputs/
model.pt  train_12345.out  train_12345.err

$ cat outputs/train_12345.out | tail -5
$ tail -5 outputs/train_12345.out
Epoch 10, Loss: 0.9823
Model saved to outputs/model.pt
==========================================
@@ -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`:

```bash
export UV_CACHE_DIR=/tudelft.net/staff-umbrella/<project>/.cache/uv
export PIXI_HOME=/tudelft.net/staff-umbrella/<project>/.pixi
export UV_LINK_MODE=copy
```

### "Module not found" in Slurm job
@@ -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

### Quick reference

```shell-session
# UV workflow
$ uv init myproject && cd myproject
$ uv init --no-package myproject && cd myproject
$ uv add torch numpy pandas
$ uv run python train.py

@@ -872,7 +900,7 @@ $ pixi add python pytorch numpy
$ pixi run python train.py

# Micromamba workflow
$ micromamba create -n myenv python=3.11 pytorch
$ micromamba create -n myenv python=3.11 pytorch -c conda-forge
$ micromamba activate myenv
$ python train.py
```