Files
MX_Pmodule/olex2

olex2

Olex2 — open-source (BSD) small-molecule crystal structure solution, refinement and analysis (olex2.refine / ShelX front-end, built on cctbx/smtbx). This module provides both the GUI and the console build:

launcher what it is
olex2 wxWidgets GUI (needs an X display)
olex2c console / "headless" Olex2 — same engine, no GUI, scriptable

How it is built

Upstream (install notes, "Linux"): "We do not provide installers for Linux – and provide zip files instead (Use the 'start' bash script to launch Olex2)" and "Nothing needs to be compiled". The official olex2-distro/1.5/olex2-linux64.zip (255 MB, ~1 GB unpacked) already contains everything (verified by listing the archive, 2026-10-16):

olex2/olex2      GUI binary                  olex2/olex2c   console binary (24 MB)
olex2/unirun     vendor auto-updater         olex2/start    vendor launcher (runs unirun, then olex2)
olex2/cctbx/     cctbx_build + cctbx_sources olex2/lib/     private CPython 3.8 + libpython, openblas, ssl, png, ...
olex2/ilib/      olex2/util/pyUtil/  olex2/etc/  olex2/sample_data/  NoSpherA2, hart, hart_mpi ...

The recipe (build):

  1. default pbuild::prep downloads the zip, three Fedora 40 rpms and a static patchelf per urls: in files/config.yaml into the distfiles cache, verifies the pinned sha256 of each and copies them to SRC_DIR (unpacker: none);
  2. configure runs unzip -t (truncated-download guard), unzips, extracts the rpms with rpm2cpio | cpio into SRC_DIR/sysroot and checks the bundled ld.so runs;
  3. install copies the vendor tree flat into $PREFIX — Olex2 derives its BaseDir() from the binary location, so the layout must stay as shipped — plus $PREFIX/sysroot, fixes modes, switches usettings.dat to update=Never, patchelf's olex2c olex2 unirun NoSpherA2 to the bundled loader (see next section), installs files/olex2-launcher twice as $PREFIX/bin/olex2 and $PREFIX/bin/olex2c, and proves with ld.so --list olex2c that the binary resolves through the bundled glibc. Only $PREFIX/bin goes on PATH.

files/olex2-launcher is the vendor start script minus the updater: it exports OLEX2_CCTBX_DIR, BOOST_ADAPTBX_*, OLEX2_GL_*, LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/ilib:$PREFIX/cctbx/cctbx_build/lib, PYTHONHOME=$PREFIX, LANG/LC_ALL=C.UTF-8, unsets PYTHONPATH (an inherited one would be imported by the embedded Python 3.8 and break it), sets LIBGL_ALWAYS_SOFTWARE=1 for the GUI only, then execs the binary. unirun is never run: /opt/psi/MX is shared and read-only, updates are done by building a new module version. The modulefile exports OLEX2_ROOT=$PREFIX (our own name; the vendor's OLEX2_DIR re-targets the updater and stays unset) and puts $PREFIX/bin on PATH.

Bundled glibc 2.39 sysroot (why and how)

Since 2026 OlexSys builds the Linux binaries on an Ubuntu 24.04-class toolchain. Scanning the archive's ELF files (2026-10-16):

file needs
olex2c, olex2, unirun GLIBC_2.38, GLIBCXX_3.4.32
NoSpherA2, lib/libopenblas.so.0, some cctbx_build/lib/*.so GLIBC_2.38
RHEL9 provides GLIBC_2.34, GLIBCXX_3.4.29
RHEL8 provides GLIBC_2.28, GLIBCXX_3.4.25

A newer glibc can not be dropped in via LD_LIBRARY_PATH: the dynamic loader path (/lib64/ld-linux-x86-64.so.2) is hard-wired into each executable and must match its libc. So the recipe:

  • ships glibc 2.39 + GCC 14 libstdc++/libgcc_s under $PREFIX/sysroot (frozen Fedora 40 rpms from kojipkgs, Fedora's permanent build archive; same glibc generation as the upstream build host). glibc and libstdc++ unpack to usr/lib64, libgcc to /lib64 (Fedora relies on the filesystem package's lib64 -> usr/lib64 symlink, which we do not extract), so configure merges lib64 into usr/lib64 and recreates the symlink — one lib dir for the RPATH;
  • runs patchelf --set-interpreter <sysroot ld.so> --force-rpath --set-rpath <sysroot libdir>:$PREFIX/lib:$PREFIX/ilib:$PREFIX/cctbx/cctbx_build/lib:$PREFIX:$ORIGIN on every dynamic executable (hart, hart_mpi are static and skipped). DT_RPATH, not RUNPATH, so that everything the embedded Python dlopen()s later resolves libc/libstdc++/openblas through the same list;
  • leaves GTK2, X11, GL, fontconfig, libcrypt etc. to the host: libraries built against an older glibc run fine on a newer one, the reverse does not.

Rules that keep this safe:

  • the sysroot lib dir is never exported in LD_LIBRARY_PATH (child processes — sh, shelxl, editors — would load the foreign libc and crash); only the patched executables see it, through their RPATH;
  • LANG=LC_ALL=C.UTF-8 in the launcher: the bundled glibc has no locale archive, C.UTF-8 is built in;
  • the originals are untouched inside the cached zip; a rebuild re-patches from scratch.

Unverified until the first RHEL9 run: NSS lookups through the host's libnss_sss under the newer libc (expected fine, NSS ABI is stable), and hart_mpi against the host MPI.

Why the binary archive and not a source build

The user-facing "open source" route is this archive: Olex2 is BSD and the official binaries are the open-source build. Compiling from https://github.com/pcxod/olex2 is not a practical module recipe: the repo builds only olex2 (GUI, wxWidgets mandatory) and unirun — there is no olex2c target in its CMakeLists/SConstruct/Makefile — the HTML GUI is in a separate SVN, and the matching cctbx/smtbx build is not published. Its own README says to take the official distribution and only swap in self-built olex2/unirun binaries. The Headless.zip/Batch.zip next to the archive on the download server are 2 KB plugin stubs, not builds.

Alternative considered: an Apptainer image (ubuntu:24.04 + gtk2/GL) that execs the host-side tree. Cleaner for the next rolling drop, but needs apptainer on every execution host and a privileged/fakeroot image build. Switch to it if the sysroot route shows locale/NSS/MPI trouble.

Building on Ra / RHEL9

module purge; module use unstable && module load modbuild/2.1.3
cd olex2
modbuild build --clean-install \
    --tmpdir=/das/work/p21/p21515/.cache/Pmodules \
    --distdir=/das/work/p21/p21515/.cache/Pmodules \
    1.5-7665
module use MX unstable && module load olex2/1.5-7665

Build host needs unzip, rpm2cpio (rpm) and cpio. Expect a silent 10–20 min for the copy of ~12 000 files onto /opt/psi; watch du -sh /opt/psi/MX/olex2/1.5-7665 from a second shell. The install step ends with olex2: <exe> -> interpreter .../sysroot/... lines and, if the build host lacks gtk2 etc., a WARNING list of host libs to install on the execution hosts.

If the download server is unreachable from the build host (plain https URLs, no login), fetch the files anywhere and pre-stage them under the exact names from files/config.yaml in <distdir>; prep then skips the download but still checks the sha256.

RHEL9 host libraries. The binaries bring their own glibc, libstdc++, python, ssl, png, jpeg, tiff, openblas, gfortran, but link system GTK2/X11/OpenGL — also olex2c (wx is linked in statically). Check once per host type:

module load olex2/1.5-7665          # exports OLEX2_ROOT=/opt/psi/MX/olex2/1.5-7665
$OLEX2_ROOT/sysroot/usr/lib64/ld-linux-x86-64.so.2 --list $OLEX2_ROOT/olex2c | grep -E 'not found|libc.so.6|libstdc'

libc.so.6/libstdc++.so.6 must resolve under $OLEX2_ROOT/sysroot; every not found line is a host package to dnf install (expected candidates on a minimal RHEL9: gtk2, mesa-libGL, mesa-libGLU, libSM, libXxf86vm, libxkbcommon). Plain ldd on the raw binary also works after the patch (it honours the new interpreter).

Verifying the install

which olex2c            # -> $OLEX2_ROOT/bin/olex2c  (= /opt/psi/MX/olex2/1.5-7665/bin/olex2c)
which olex2             # -> $OLEX2_ROOT/bin/olex2
cat $OLEX2_ROOT/version.txt   # SVN Revision No. 7665

# console: starts, loads a shipped sample, refines, exits (writes to ~/.olex2
# and to a scratch copy of the sample - run it in a scratch dir)
cd $(mktemp -d) && cp $OLEX2_ROOT/sample_data/sucrose/* .
olex2c                  # interactive prompt; then type:
#   reap sucrose.res
#   refine 4
#   exit

# GUI (needs X / x2go / NoMachine session):
olex2

Scripting olex2c: it reads commands from stdin, so a batch job can do printf 'reap %s\nrefine 10\nexit\n' x.ins | olex2c (to be confirmed on the first RHEL9 test — the Windows olex2c.exe is used this way by CrysAlisPro and the upstream NoSpherA2 release tests). The headless engine loads macrox.xld instead of macro.xld; GUI-only python calls print "not available in Headless Olex" rather than fail.

First-start notes: Olex2 keeps user data in ~/.olex2 (DataDir), copies sample data there and may try to delete runonce.* markers in BaseDir (read-only for users — harmless). The vendor start and unirun remain under $PREFIX but are intentionally not on PATH; do not run them against the shared install.

Adding a new version

1.5/olex2-linux64.zip is a rolling file — OlexSys replaces it in place (check Modified on https://secure.olex2.org/olex2-distro/1.5/, or the ALPHA/BETA series folders). The sha256 pinned in files/config.yaml will therefore fail against a newer drop; that is the signal to add a version, not to edit the hash:

  1. download the current zip and read its revision and hash: unzip -p olex2-linux64.zip olex2/version.txt → e.g. SVN Revision No. 7800, sha256sum olex2-linux64.zip;
  2. check the new binaries' glibc need still fits the bundled 2.39: unzip -p olex2-linux64.zip olex2/olex2c | strings | grep -o 'GLIBC_2\.[0-9]*' | sort -uV | tail -1 (and GLIBCXX_3\.4\.[0-9]* ≤ 3.4.33). If upstream moved past that, bump the three rpms in urls:/shasums: to a newer frozen Fedora release (kojipkgs packages/glibc/<ver>/<rel>/x86_64/, packages/gcc/...);
  3. add 1.5-7800 under versions: (relstage: unstable) and olex2-1.5-7800-linux64.zip: <sha256> under shasums: — the urls: entry needs no change (${V_PKG} = series dir, ${V} = cache name); for a 1.6 series just use 1.6-<rev> and the URL follows;
  4. build as above; promote to stable only after olex2c and olex2 have been exercised on the target host.

History: 1.5 (stable) is the older Ra/RHEL8 module built by the previous recipe from a hand-downloaded olex2-linux64.zip placed next to build (never committed, *.zip is gitignored); it is kept in config.yaml so the installed module stays listed. Do not rebuild it with this recipe.