How to Compute and Set the UB Matrix#

The \(UB\) orientation matrix encodes how the crystal is mounted on the diffractometer and is required before any forward() calculation can succeed. This guide covers the common tasks: computing \(UB\) from reflections, setting it manually, inspecting and refining it, and resetting it.

See also

Tutorial: Your First Diffractometer — step-by-step walkthrough of the full orientation workflow for a new user.

Constraints — filter forward() solutions after the UB matrix is set.

Architecture & Design Decisions — explanation of the \(B\), \(U\), and \(UB\) matrices.

hkl_soleil UB matrix : calculate from 2 reflections. — executable notebook: compute \(UB\) from two reflections using the hkl_soleil solver.

hkl_soleil UB matrix : Set directly — executable notebook: set \(UB\) directly from a known matrix.

Setup#

All examples use a simulated 4-circle diffractometer with silicon:

>>> import hklpy2
>>> from hklpy2.user import (
...     add_sample, calc_UB, or_swap, pa,
...     remove_reflection, set_diffractometer, setor,
... )
>>> fourc = hklpy2.creator(name="fourc", geometry="E4CV", solver="hkl_soleil")
>>> set_diffractometer(fourc)
>>> add_sample("silicon", a=hklpy2.SI_LATTICE_PARAMETER)
>>> fourc.beam.wavelength.put(1.54)

How do I add orientation reflections?#

Use setor() to record a reflection — the measured motor angles for a known \((h, k, l)\) position:

>>> r1 = setor(4, 0, 0, tth=69.0966, omega=-145.451, chi=0, phi=0)
>>> r2 = setor(0, 4, 0, tth=69.0966, omega=-145.451, chi=90, phi=0)

setor() uses the diffractometer’s current wavelength at the time it is called. To record a reflection measured at a different wavelength, pass wavelength= explicitly:

>>> r3 = setor(0, 0, 4, tth=69.0966, omega=-145.451, chi=90, phi=90,
...            wavelength=1.00)

For lower-level control, add_reflection() on fourc.core accepts the same arguments and returns a Reflection object directly.

How do I compute UB from two reflections?#

Call calc_UB() with the two reflection objects returned by setor():

>>> calc_UB(r1, r2)
[[-1.4134285e-05, -1.4134285e-05, -1.156906937382],
 [0.0, -1.156906937469, 1.4134285e-05],
 [-1.156906937469, 1.73e-10, 1.4134285e-05]]

The matrix is stored on the sample and pushed to the solver automatically. You can also pass reflection names instead of objects:

>>> calc_UB(r1.name, r2.name)

Or call the equivalent method directly on Core:

>>> fourc.core.calc_UB(r1, r2)

How do I swap the two orienting reflections?#

or_swap() swaps the first two reflections in the orientation list and recomputes \(UB\). This is equivalent to SPEC’s or_swap command:

>>> or_swap()

The new \(UB\) matrix is returned and stored automatically.

How do I set UB manually from a known matrix?#

Assign directly to UB on the sample. A plain nested Python list is sufficient — a numpy array is accepted but not required:

>>> fourc.core.sample.UB = [
...     [0.0,       0.0,      -1.1569],
...     [0.0,      -1.1569,    0.0   ],
...     [-1.1569,   0.0,       0.0   ],
... ]

The solver is updated automatically on the next forward() or inverse() call.

How do I inspect the current UB matrix?#

Call pa() to see the full diffractometer state including the \(U\) and \(UB\) matrices:

>>> pa()

Or access the matrix directly:

>>> fourc.core.sample.UB

How do I remove a reflection?#

Use remove_reflection() with the reflection’s name:

>>> remove_reflection(r1.name)

To remove all reflections and reset the sample to defaults:

>>> fourc.core.reset_samples()

Note

reset_samples() removes all samples and reflections and recreates a single default sample. Use remove_reflection() if you only want to discard one reflection while keeping others.

How do I refine the lattice parameters?#

If the solver supports it, refine_lattice() uses three or more reflections to refine the lattice parameters:

>>> r3 = setor(0, 0, 4, tth=69.0966, omega=-145.451, chi=90, phi=90)
>>> fourc.core.refine_lattice(r1, r2, r3)

The refined Lattice is returned and stored on the sample. Not all solvers implement lattice refinement — check the solver documentation if this raises NotImplementedError.

How do I verify the UB matrix is correct?#

The standard check is to confirm that inverse() at the reflection angles recovers the expected \((h, k, l)\), and that forward() at the expected \((h, k, l)\) returns angles close to the measured ones:

>>> # inverse: angles → hkl
>>> fourc.inverse(dict(omega=-145.451, chi=0, phi=0, tth=69.0966))
Hklpy2DiffractometerPseudoPos(h=3.9999, k=0, l=0)   # ≈ (4, 0, 0) ✓

>>> # forward: hkl → angles
>>> fourc.forward(4, 0, 0)
Hklpy2DiffractometerRealPos(omega=-34.5491, chi=0.0, phi=-110.9011, tth=69.0982)

Small differences (last decimal place) are normal floating-point rounding. Large differences indicate the orientation reflections were recorded incorrectly or the wrong geometry was selected.