JUPITER setup: native LAMMPS and ALCHEMI

Rows of JUPITER Booster computing racks.

JUPITER Booster racks. Photo: Forschungszentrum Jülich / Sascha Kreklau; source and license details (CC BY 4.0). This copy was resized; the photo does not imply institutional endorsement.

Note

JUPITER hardware According to the JUPITER Booster configuration, each booster node has four GH200 superchips; each combines a 72-core Arm CPU with a GPU with 96 GB of high-bandwidth memory.

This profile has passed short one-, two-, four-, and eight-GPU silicon runs with the pinned MACE ML-IAP export; the eight-GPU run used two nodes. These are functional checks, not throughput or scientific-equivalence benchmarks. Both cells in B5 completed through a private Jupyter kernel. A short private JupyterLab server passed TLS-fingerprint and authenticated-API checks, then its allocation was cancelled. Do not use an Arrhenius SIF or MPI binary here. Native ALCHEMI has separately passed one-GPU, one- and eight-trajectory five-step NVE smokes. Neither smoke is a throughput benchmark.

The tested native module family is:

module purge
module load Stages/2025 GCC/13.3.0 CMake/3.29.3 CUDA/12 \
  OpenMPI/5.0.5 Python/3.12.3 PyTorch/2.5.1

Use a private project directory outside the lesson Git repository, for example /e/project1/<PROJECT>/<USER>/mlip-hands-on. Put the pinned LAMMPS source archive and model in its inputs/ directory; check their hashes in reference/model.toml and the build script. Set MLIP_JUPITER_ROOT to that private directory. The candidate builder verifies the source archive, then builds OpenMPI, CUDA/Hopper Kokkos, ML-IAP, and a shared LAMMPS library:

export MLIP_JUPITER_ROOT=/e/project1/<PROJECT>/<USER>/mlip-hands-on
sbatch --account=<PROJECT> scripts/build-jupiter-lammps-mpi.sbatch

Do not repeat a submission if its outcome is uncertain. Check the job and the candidate directory first. A completed build is not yet a run. Package its Python wrapper from the same pinned source and shared library:

export MLIP_JUPITER_BUILD_ID=<completed-build-job-id>
bash scripts/package-jupiter-lammps-python.sh

The wrapper goes into a private Python environment; the script never installs it into the site modules. That environment needs the MACE checkpoint dependencies and cuequivariance plus cuequivariance-torch 0.11.1 for the validated ML-IAP export. The latter two versions match the tested exporter environment. A working import lammps alone does not establish a GPU run.

JUPITER’s batch shell can see all four GPUs even when the job requested one. The tested one-GPU script runs its Python calculation inside srun --gpus=1, where PyTorch sees one device. Do not replace that step with a direct batch-shell Python call when testing this lesson’s one-GPU code.

The pinned original checkpoint did not export locally with the first JUPITER MACE environment: its conversion failed while loading symmetric-contraction weights. The one-GPU runtime check instead used an independently hashed ML-IAP export made from the same checkpoint. Recheck the export recipe before teaching local export as a completed JUPITER step. The source and runtime must remain two separate claims.

For a short check of that exact export, put it in the private inputs/ directory as mace-mliap-db578c55.pt, stage this repository outside the allocation, and submit the smoke script with both paths in the Slurm environment:

export MLIP_LESSON_ROOT=/path/to/staged/mlip-hands-on
sbatch --account=<PROJECT> --export=ALL,MLIP_JUPITER_ROOT,MLIP_LESSON_ROOT,MLIP_JUPITER_BUILD_ID \
  scripts/test-jupiter-lammps-verified.sbatch

The smoke runs 64 silicon atoms for five measured NVE steps. It checks that LAMMPS, ML-IAP/Kokkos, the wrapper, model and GPU step execute together; it does not measure throughput or establish scientific agreement. Do not submit it until the private export has been verified against the hash in the script.

For one coupled 512-atom trajectory, the MPI script uses a distinct Slurm GPU binding on each rank. JUPITER’s documented job examples request both --gres and --gpus-per-task=1; each rank then sees its own GPU as device zero. The shared-visibility mode used on Arrhenius is not selected here. For example, the one-node four-GPU functional check is:

sbatch --account=<PROJECT> --nodes=1 --ntasks=4 \
  --gres=gpu:4 --gpus-per-task=1 --cpus-per-task=8 \
  --mem=256G --time=00:15:00 \
  --export=ALL,MLIP_JUPITER_ROOT,MLIP_LESSON_ROOT,MLIP_JUPITER_BUILD_ID \
  scripts/test-jupiter-lammps-mpi.sbatch

The same script accepts one node with one or two ranks, or two nodes with eight ranks (four per node). Change the --nodes, --ntasks, --gres, and --ntasks-per-node requests together; --gres=gpu:4 is per node for the two-node case. It runs ten measured NVE steps, writes one private result per rank, and verifies completion. On this system a partial-GPU request can still allocate an exclusive whole node, so do not read the requested GPU count as the billed allocation. Inspect current policy and queue before submitting.

The tested one-node two- and four-GPU and two-node eight-GPU runs completed. At 512 atoms and ten measured steps, rank-wise final energies were internally identical within each job but differed slightly across rank counts. These checks do not establish decomposition-independent scientific agreement or useful scaling.

Native ALCHEMI without the container group

JUPITER compute nodes have no outbound internet. Download the pinned Python packages from a login node into a private wheelhouse; the locked build and runtime requirements are in locks/. The download helper checks both lock hashes and does not install packages:

export MLIP_LESSON_ROOT=/path/to/staged/mlip-hands-on
export MLIP_WHEELHOUSE_OUTPUT=/e/project1/<PROJECT>/<USER>/mlip-hands-on/alchemi-wheels.tar
bash scripts/prepare-jupiter-wheelhouse.sh
sha256sum "$MLIP_WHEELHOUSE_OUTPUT"

Record the resulting SHA-256 and use that exact archive for an offline, fresh virtual environment on a booster node. Do not put the wheelhouse, logs, or environment in Git. The script verifies the archive and both locks before installation; it does not replace a working environment:

export MLIP_JUPITER_WHEELHOUSE="$MLIP_WHEELHOUSE_OUTPUT"
export MLIP_JUPITER_WHEELHOUSE_SHA256=<reviewed-archive-sha256>
sbatch --account=<PROJECT> \
  --export=ALL,MLIP_JUPITER_ROOT,MLIP_LESSON_ROOT,MLIP_JUPITER_WHEELHOUSE,MLIP_JUPITER_WHEELHOUSE_SHA256 \
  scripts/prepare-jupiter-alchemi-env.sbatch

After that job completes successfully, set MLIP_JUPITER_ALCHEMI_ENV_ID to its job ID. Put the original MACE checkpoint named in reference/model.toml in the private inputs/ directory. A short GPU check uses the same silicon trajectory as the Arrhenius example:

export MLIP_JUPITER_ALCHEMI_ENV_ID=<completed-environment-job-id>
export MLIP_JUPITER_REPLICAS=1
sbatch --account=<PROJECT> \
  --export=ALL,MLIP_JUPITER_ROOT,MLIP_LESSON_ROOT,MLIP_JUPITER_ALCHEMI_ENV_ID,MLIP_JUPITER_REPLICAS \
  scripts/test-jupiter-alchemi.sbatch

Set MLIP_JUPITER_REPLICAS=8 for the separate batched functional check. Both replica counts completed on one GPU with the pinned checkpoint. The smoke script refuses other replica counts; adjust it only after reviewing a new test. These checks prove that the pinned native environment, GPU, model, and short trajectories work together. They do not establish comparative speed, temperature equilibration, or scientific agreement with LAMMPS.

The same scripts/run-alchemi.sh used by the MyST notebook cells accepts an explicit native mode on JUPITER. In an allocated GPU step, set:

export MLIP_ALCHEMI_RUNNER=native
export MLIP_NATIVE_ALCHEMI_PYTHON="$MLIP_JUPITER_ROOT/env-alchemi-$MLIP_JUPITER_ALCHEMI_ENV_ID/bin/python"
export MLIP_MODEL="$MLIP_JUPITER_ROOT/inputs/mace-mp-0a-small-2ddb079cee0e131e.model"

The eight-replica short smoke and both cells in B5 passed through that exact runner. Launch the notebook only inside a GPU-bound srun step so the kernel sees one GPU.

For a private JupyterLab environment, build the course’s patched MyST wheel as described in jupyterlab-enccs/README.md and stage it under the private root. The preparation script checks the exact wheel identity, then installs Jupytext and Matplotlib alongside the site’s JupyterLab 4.3.4 in a fresh private environment:

export MLIP_JUPITER_MYST_WHEEL="$MLIP_JUPITER_ROOT/inputs/jupyterlab_myst-2.4.2-py3-none-any.whl"
export MLIP_JUPITER_MYST_WHEEL_SHA256=<reviewed-wheel-sha256>
export MLIP_JUPITER_JUPYTER_ENV="$MLIP_JUPITER_ROOT/jupyter-env-course"
bash scripts/prepare-jupiter-jupyter-env.sh

After reviewing the private environment and source lesson, submit one bounded GPU notebook session. The direct Slurm form below requires MLIP_JUPITER_ROOT to be owner-only because it stores a token-bearing log there. The %j placeholder is replaced by Slurm with the job ID:

sbatch --account=<PROJECT> \
  --output="$MLIP_JUPITER_ROOT/jupyter-%j.log" \
  --error="$MLIP_JUPITER_ROOT/jupyter-%j.err" \
  --export=ALL,MLIP_JUPITER_ROOT,MLIP_LESSON_ROOT,MLIP_JUPITER_ALCHEMI_ENV_ID,MLIP_JUPITER_JUPYTER_ENV \
  scripts/jupiter-jupyter.sbatch

The preferred helper keeps logs in their own owner-only directory, validates the environment paths, and prints the one job ID without showing the token:

export MLIP_ACCOUNT=<PROJECT>
export MLIP_JOB_LOG_DIR="$MLIP_JUPITER_ROOT/private-jupyter-logs"
bash scripts/submit-jupiter-jupyter.sh

From your laptop, use the notebook mode of scripts/connect-from-laptop.sh with your JUPITER SSH alias and job ID to verify the private TLS fingerprint and forward the notebook. Cancel the allocation when finished; closing the browser does not stop Slurm.

Metatomic MACE in native LAMMPS/Kokkos

This is a separate LAMMPS pair style from the ML-IAP route above. The pinned Metatomic fork needed a four-file compatibility patch: two Kokkos classes still declared a device-picker override removed from their base classes. The patch removes only those stale declarations and dead definitions; it does not change the force calculation. Keep the original checkpoint, exported Metatomic model, package overlays, and LAMMPS build outside Git.

On a login node, prepare the pinned fork and private Python overlays:

export MLIP_LESSON_ROOT=/path/to/staged/mlip-hands-on
export MLIP_METATOMIC_SOURCE="$MLIP_JUPITER_ROOT/lammps-metatomic-patched-6a3910424d0aeccf27ad1fc233be1933f72631d2"
export MLIP_METATOMIC_OVERLAY="$MLIP_JUPITER_ROOT/metatomic-overlay-course"
export MLIP_MACE_EXPORT_OVERLAY="$MLIP_JUPITER_ROOT/mace-export-overlay-course"
bash scripts/prepare-jupiter-metatomic-source.sh
bash scripts/prepare-jupiter-metatomic-env.sh

The exporter uses the pinned original MACE checkpoint, zero training epochs, and a tiny dummy silicon structure to make a Metatomic-format model. The dummy structure supplies the export interface; it is not training data for a new potential. Record the printed SHA-256 of the fresh export instead of assuming exports are byte-identical:

export MLIP_MACE_MODEL="$MLIP_JUPITER_ROOT/inputs/mace-mp-0a-small-2ddb079cee0e131e.model"
export MLIP_METATOMIC_EXPORT_DIR="$MLIP_JUPITER_ROOT/metatomic-export-course"
bash scripts/export-jupiter-metatomic-model.sh

Build the MPI/CUDA/Hopper LAMMPS executable on one booster node. The script checks the four patched source hashes and links against the NCCL bundled with the pinned PyTorch environment, rather than an older site NCCL:

sbatch --account=<PROJECT> \
  --export=ALL,MLIP_JUPITER_ROOT,MLIP_METATOMIC_SOURCE,MLIP_METATOMIC_OVERLAY,MLIP_JUPITER_ALCHEMI_ENV_ID \
  scripts/build-jupiter-metatomic-mpi.sbatch

Set MLIP_JUPITER_METATOMIC_BUILD_ID to the completed build job ID, MLIP_METATOMIC_MODEL to the private exported model.pt, and MLIP_METATOMIC_MODEL_SHA256 to its recorded hash. The smoke script checks all three identities. Its default input is a 64-atom silicon NVE trajectory; for multi-GPU runs, set MLIP_METATOMIC_INPUT to the staged examples/lammps_metatomic_si_512.in instead. The default 64-atom input is:

 1# Short silicon NVE smoke with an exported Metatomic MACE model.
 2# Supply the private model path as: -var model /path/to/model.pt
 3package kokkos newton on neigh half
 4units metal
 5atom_style atomic/kk
 6boundary p p p
 7
 8lattice diamond 5.43
 9region box block 0 2 0 2 0 2 units lattice
10create_box 1 box
11create_atoms 1 box
12mass 1 28.085
13
14pair_style metatomic/kk ${model}
15pair_coeff * * 14
16timestep 0.0001
17velocity all create 300.0 37214 mom yes rot yes dist gaussian
18fix integrate all nve/kk
19thermo_style custom step temp pe ke etotal
20thermo 1
21run_style verlet/kk
22run 5

The complete 64-atom input is shown above; the 512-atom input changes the diamond-lattice box from 0 2 to 0 4 in each direction. units metal makes the timestep unit picoseconds, so 0.0001 is 0.1 fs. pair_style loads the exported model passed as ${model}, while pair_coeff * * 14 maps the one atom type to silicon’s atomic number. The nve/kk fix integrates without a thermostat; thermo 1 prints diagnostic quantities each step, and run 5 advances only five steps. The job script supplies the model variable and launches the MPI ranks with srun. In isolation, the corresponding LAMMPS command is:

lmp -k on g 1 -sf kk -var model "$MLIP_METATOMIC_MODEL" \
  -in "$MLIP_METATOMIC_INPUT"

That line is an explanation of the input interface, not a replacement for the validated Slurm job: its GPU count and rank binding come from the allocation. Opening this notebook does not submit it.

A two-GPU job request, to issue manually after checking the site policy, is:

export MLIP_METATOMIC_INPUT="$MLIP_LESSON_ROOT/examples/lammps_metatomic_si_512.in"
sbatch --account=<PROJECT> --nodes=1 --ntasks=2 --ntasks-per-node=2 \
  --gres=gpu:2 --gpus-per-task=1 --cpus-per-task=8 --mem=128G \
  --export=ALL,MLIP_JUPITER_ROOT,MLIP_LESSON_ROOT,MLIP_JUPITER_METATOMIC_BUILD_ID,MLIP_METATOMIC_MODEL,MLIP_METATOMIC_MODEL_SHA256,MLIP_METATOMIC_OVERLAY,MLIP_JUPITER_ALCHEMI_ENV_ID,MLIP_METATOMIC_INPUT \
  scripts/test-jupiter-metatomic-mpi.sbatch

One, two, and four GPUs on one node and eight GPUs across two nodes passed short functional runs with this Metatomic fork. For four GPUs, request four ranks and four GPUs on one node. For eight, request two nodes, four ranks and four GPUs per node, and eight total ranks. These five-step smokes do not measure scaling, establish cross-engine force agreement, or prove a long trajectory stable. The earlier ML-IAP/Kokkos path remains the reviewed performance baseline until a separate matched benchmark is completed.

The container-group route is intentionally omitted from this profile. Neither native LAMMPS nor this native ALCHEMI environment needs that group.