Installation¶
rietx needs Python 3.11 or newer and installs from PyPI. Install it into a
virtual environment: numba carries an upper bound on numpy, which is easier
to satisfy per project than across one shared environment.
With uv, which creates the environment and installs in one tool:
uv venv --python 3.12
uv pip install rietx
source .venv/bin/activate
With pip:
python3 -m venv .venv
source .venv/bin/activate
pip install rietx
With uv, which creates the environment and installs in one tool:
uv venv --python 3.12
uv pip install rietx
source .venv/bin/activate
With pip:
python3 -m venv .venv
source .venv/bin/activate
pip install rietx
With uv, which creates the environment and installs in one tool:
uv venv --python 3.12
uv pip install rietx
.venv\Scripts\activate
With pip:
py -m venv .venv
.venv\Scripts\activate
pip install rietx
Check it:
python -c "import rietx; print(rietx.__version__)"
That prints the version you installed, 1.6.0.dev0 for the copy this manual was built from. A first refinement is the first refinement.
Requirements¶
Requirement |
Purpose |
|---|---|
Python ≥ 3.11 |
|
|
the fp64 arrays the core computes in |
|
the trust-region least-squares solver |
|
the schemas: validation, defaults, JSON round-trip |
|
CIF reading, space groups, symmetry operations |
|
site symmetry, Wyckoff positions, cell reduction |
|
compiles the peak kernels (The compiled kernels) |
Those six are the whole install, and nothing in that list is optional or lazily imported. It reads patterns and CIFs, applies every correction, runs the staged refinement machinery, builds the report, indexes an unknown cell, and keeps projects and history.
Optional extras¶
No extra changes a refined number. Name one in brackets to install it, quoting
the argument because zsh reads bare brackets as a glob:
pip install "rietx[viz]" # one extra
pip install "rietx[viz,jax]" # several, comma-separated, no spaces
uv pip install -e ".[dev]" # from a source checkout
Extra |
Installs |
Purpose |
|---|---|---|
|
matplotlib |
Plots. |
|
nothing |
Empty, and kept so an existing |
|
jax |
The |
|
torch |
Experimental. |
|
sphinx, myst-parser, sphinxcontrib-bibtex, sphinx-design, furo |
Builds this manual. |
|
the |
The test suite. |
Note
backend="numpy" is the default and the only backend a refinement needs. The
others hold the analytic Jacobian to an independent account: an Apple-GPU
refinement runs 46 to 182 times slower than numpy, because the work is
launch-latency-bound. Precision is not the trade either way. A GPU backend may
compute Jacobian columns in fp32, but the residual used for the cost and the
statistics, and the solve itself, stay fp64 on the host.
The compiled kernels¶
The peak profile, its derivatives and the accumulation that scatters them onto the pattern are evaluated by compiled kernels rather than by numpy expressions. They are on by default and there is nothing to install or select. Measured on a four-phase Cu Kα refinement they take the fit from 17.6 s to 8.9 s; on a three-phase one, from 4.2 s to 2.2 s; on a two-phase synchrotron pattern with no axial divergence, from 0.54 s to 0.40 s.
The first process on a machine pays a 0.6 s compile, and every later one pays
0.12 s to load the result from ~/.rietx/numba-cache (or from
$RIETX_STATE_DIR if that is set). Most of even that overlaps with other work:
the compile starts on a background thread when the model is compiled, and runs
while the file is read and the parameter table is built.
The dichotomy indexing engine’s box search runs on a compiled kernel too. It searches the same boxes in the same order as the numpy loop, so it finds the same candidates, bit for bit, and only the time changes. How much depends on how many free metric parameters the crystal system has. On a synthetic monoclinic list (four) the engine takes 9.99-10.02 s where it took 195-206 s. On the round-robin brucite and corundum patterns (hexagonal, trigonal and tetragonal, two each) it saves 3-8 %, because most of their time goes to refining the cells the search finds rather than to the search. Real monoclinic data is the same once the search is fast: a bethanechol search cut at its 30 s budget tests 13 % more boxes and reaches the same answer. That kernel is compiled the first time an indexing search needs it, 4.4-4.6 s on the first run on a machine and 0.23-0.27 s after that, before any crystal system’s time budget starts.
Turn the kernels off with RIETX_COMPILED=0, which needs no reinstall. Every
kernel has the numpy expression it replaces standing behind it, so refinements
and indexing searches run correctly, only slower. Use it on a machine where the
compiler misbehaves, or for a run that has to reproduce another one exactly.
RIETX_COMPILED=0 python my_refinement.py
For a smaller install, leave numba out altogether and take the numpy path
permanently. It is 157 MB of the install, 137 MB of that llvmlite, against a
124 MB baseline:
pip install --no-deps rietx
pip install numpy scipy pydantic gemmi spglib
capabilities().features answers the two questions separately, because they can
disagree: compiled_kernels is whether numba imports here, and
compiled_kernels_active is whether the next refinement will use it.
from rietx import capabilities
caps = capabilities()
caps.features["compiled_kernels"]
caps.features["compiled_kernels_active"]
The compiled and numpy paths agree to within one or two units in the last place,
everywhere and on every platform. The accumulation is bit-for-bit identical,
being multiplication and addition in a fixed order with no library function in
it. The peak shapes call exp, whose last bit belongs to whichever library
provides it, so they land on the same doubles on some platforms and one part in
3e-17 away on others; peaks carrying the axial-divergence correction differ by
about 1e-16 on all of them, a different summation order for the same quadrature.
None of this is visible in a refined parameter or its esd.
Checking an install¶
Ask the package rather than a table that goes stale. capabilities() reports
the versions, the backends, the plans, the modes, the anodes, the pattern
formats it can open, and the feature flags. For each backend it reports whether
the optional dependency imports here:
from rietx import capabilities
caps = capabilities()
caps.package_version
[backend.name for backend in caps.backends if backend.available]
[fmt.name for fmt in caps.reader_formats]
Capabilities.backends is the field that answers “did my jax extra take?”.
Each BackendCapability carries BackendCapability.available (does it import
here), BackendCapability.requires (the distribution to install) and
BackendCapability.experimental. Calling rietx from a program covers the rest of the object.
rietx.__version__ is the version of the installed distribution, the same
string capabilities().package_version reports and every Provenance,
TreeHeader and project.json is stamped with, so a result and the package
that produced it can never disagree about it. A source checkout is the one
exception. If its pyproject.toml has moved on since its editable install,
the source’s version is stamped instead, with the commit as a local label (see
Troubleshooting).
Installing from source¶
Install from source to contribute, or to run against an unreleased change:
git clone https://github.com/yue-here/rietx
cd rietx
uv venv --python 3.12 && uv pip install -e ".[dev]"
Then run the suite. The fast selection is the unit and property tests. The full selection adds the real-data acceptance suites, which refine certified standards and take tens of minutes:
.venv/bin/python -m pytest -n auto --dist loadgroup -m "not slow" # fast
.venv/bin/python -m pytest -n auto --dist loadgroup # everything
--dist loadgroup is not optional. It honours the marks that keep a shared
refinement fixture on one worker; plain --dist load silently refits, so the
suite refuses a parallel run without it and names what to pass. The suite
prints its own counts, and those counts depend on the extras installed: jax and
torch turn skips into passes.
Troubleshooting¶
zsh: no matches found: rietx[viz]. zsh expanded the brackets as a glob.
Quote the argument: pip install "rietx[viz]".
rietx.__version__ reads 0.0.0+dev. No distribution of that name is
installed, and what you imported is a source checkout sitting on sys.path
ahead of its own install. Install it (uv pip install -e .) before refining
anything: that string is stamped into the provenance of every result, every
history tree and every project file the session writes.
RuntimeWarning: the installed rietx metadata says '1.4.0' but the source tree it is imported from says '1.6.0.dev0'. The checkout’s version was bumped after
its editable install, which writes its metadata once, when it is installed.
Results are stamped with the source version and the commit
(1.6.0.dev0+g and twelve hex digits, .dirty if a tracked file was edited),
so they still name the code that ran. Reinstall (uv pip install -e ".[dev]")
to make the two agree.
pip cannot find a version of numba for your numpy. numba carries an
upper bound on numpy (numpy<2.6 as of numba 0.63), so a very new numpy has
to wait for a numba that admits it. Either pin numpy below the ceiling in this
environment, or install without numba as above and run the numpy path.
Validation and accuracy claims¶
docs/VALIDATION.md
tabulates every real-data assertion in the repository and says what each
tolerance is referenced to. It is generated from the suite, so it is the
accuracy claim, and nothing in this manual restates it.
It opens with the rule to read it under: judge a correction by what it changed, never by ΔRwp. Of the eight corrections in v0.5, two provably cannot move Rwp, one moves it the wrong way when it is right, and the two largest accuracy wins are invisible in it.