Architecture
The integration uses two Python processes with a deliberate boundary between them.
flowchart TD
A[SWAN session starts] --> B[Parent Jupyter server<br/>SWAN LCG Python]
A --> C[swan_env.sh]
C --> D[Expose repository bin directory]
C --> E[Render combine-v11 into<br/>session KERNEL_DIR]
E --> F[Kernel appears in picker]
F --> G[combine-swan-kernel]
G --> H[Clear inherited<br/>PYTHONHOME and PYTHONPATH]
H --> I[Activate CMSSW_16_0_0<br/>with SCRAM]
I --> J[Preload PyROOT in batch mode]
J --> K[Start CMSSW ipykernel]
K --> L[Notebook imports and<br/>Combine commands]
B -. remains unchanged .-> L
Why the server and kernel are separate
SWAN starts Jupyter with the selected LCG Python stack. CMSSW ships its own Python interpreter, packages, ROOT build, and shared libraries. Loading the full CMSSW runtime into the parent server can replace libraries that Jupyter itself is already using.
swan_env.sh therefore performs only server-safe setup:
- exports
COMBINE_SWAN_ROOT; - adds the repository's
bindirectory toPATH; - renders a server-visible kernelspec in SWAN's
KERNEL_DIR.
The selected kernel then activates CMSSW in a new process.
Two kernel registries
| Registry | Purpose | Lifetime |
|---|---|---|
$CERNBOX_HOME/.local/share/jupyter/kernels/combine-v11 |
Persistent user copy installed by scripts/install_kernel.sh. |
Across SWAN sessions. |
$KERNEL_DIR/combine-v11 |
Copy visible to the currently running SWAN notebook server. | Recreated for each session. |
A terminal can find the persistent copy with jupyter kernelspec list even when the already-running notebook server cannot. Staging the second copy is what makes the kernel appear in the picker.
Python environment isolation
SWAN may export PYTHONHOME and PYTHONPATH for its LCG interpreter. If those values leak into CMS or SCRAM commands, another Python executable can be forced to load standard-library paths from an incompatible Python version. The characteristic early failure is ModuleNotFoundError: No module named 'encodings'.
Both installation and activation clear those variables before loading CMS/SCRAM. This is intentional, not a generic recommendation to erase Python paths in unrelated workflows.
PyROOT bootstrap
The kernel launcher activates CMSSW and then runs scripts/kernel_bootstrap.py. That bootstrap imports ROOT and selects batch mode before starting IPython's kernel application.
Preloading ROOT outside IPython avoids initializing JupyROOT's additional capture/event layer. The notebook still receives normal PyROOT objects, while headless execution avoids the deadlock behavior that motivated this bootstrap.
Component map
| Component | Responsibility |
|---|---|
config/defaults.sh |
Holds the supported release pins and Combine source URL. |
install.sh |
Creates CMSSW, checks out Combine, builds, registers the kernel, and validates. |
activate.sh |
Switches an explicitly sourced terminal into the pinned CMSSW runtime. |
swan_env.sh |
Performs parent-server-safe setup and session kernel staging. |
bin/combine-swan-kernel |
Activates CMSSW inside the dedicated kernel process. |
scripts/kernel_bootstrap.py |
Preloads PyROOT and starts ipykernel. |
scripts/install_kernel.sh |
Installs persistent and current-session kernelspec copies. |
scripts/smoke_test.py |
Runs the end-to-end runtime validation. |