How to use the ad_hoc solver#

This guide shows how to create a diffractometer with the ad_hoc solver, orient a crystalline sample, and compute reciprocal-space positions. It assumes you have already installed the package.

The ad_hoc solver wraps the ad_hoc_diffractometer library. This library provides 10 diffractometer geometries ranging from Eulerian four-circle to six-circle, kappa, and surface configurations. See ad_hoc solver for the full list.

Create a diffractometer#

Four-circle vertical (the default geometry):

import hklpy2

fourc = hklpy2.creator(
    solver="ad_hoc",
    geometry="fourcv",
    name="fourc",
)

The object fourc has four real axes (omega, chi, phi, ttheta) and three pseudo axes (h, k, l).

Six-circle (psic):

psic = hklpy2.creator(
    solver="ad_hoc",
    geometry="psic",
    name="psic",
)

Kappa geometry (set the kappa tilt angle via solver_kwargs):

kappa = hklpy2.creator(
    solver="ad_hoc",
    geometry="kappa4cv",
    name="kappa",
    solver_kwargs={"kappa_alpha_deg": 50},
)

Set the crystal lattice#

fourc.add_sample(name="silicon", a=hklpy2.SI_LATTICE_PARAMETER)
fourc.beam.wavelength.put(1.0)

Add orientation reflections#

Provide two reflections measured at known motor positions:

import math

theta = math.degrees(math.asin(1.0 / (2 * 5.431)))
tth = 2 * theta

r1 = fourc.add_reflection(
    pseudos={"h": 1, "k": 0, "l": 0},
    reals={"omega": theta, "chi": 0, "phi": 0, "ttheta": tth},
    wavelength=1.0,
    name="r1",
)
r2 = fourc.add_reflection(
    pseudos={"h": 0, "k": 1, "l": 0},
    reals={"omega": theta, "chi": 0, "phi": 90, "ttheta": tth},
    wavelength=1.0,
    name="r2",
)

Calculate the UB matrix#

fourc.core.calc_UB(r1, r2)

Choose an operating mode#

The default mode for fourcv is bisecting. To change it:

fourc.core.mode = "fixed_phi"

See ad_hoc solver for the full mode tables for each geometry.

Compute motor positions (forward)#

fourc.forward(1, 0, 0)

This returns a single chosen motor-position solution for the given (h, k, l) (an Hklpy2DiffractometerRealPos). The underlying solver’s forward() may return multiple solutions; the diffractometer picks one according to the policy assigned to fourc._forward_solution (defaults to hklpy2.utils.pick_first_solution()). The complete list of solutions can be returned from fourc.core.forward((1, 0, 0)). See the upstream hklpy2 guide How to Choose the Default forward() Solution for details.

Tip

The two call shapes differ: fourc.forward(1, 0, 0) takes h, k, l as separate positional arguments, while fourc.core.forward((1, 0, 0)) takes a single sequence (tuple / list / ndarray) or dict (e.g. {"h": 1, "k": 0, "l": 0}).

Compute (h, k, l) from motor positions (inverse)#

fourc.inverse(fourc.real_position)

This returns the (h, k, l) values computed from the supplied motor positions. fourc.real_position is the current readout of all real axes; pass a different set of values to compute (h, k, l) at a hypothetical position instead.

Available geometries at a glance#

Geometry

Real axes

Modes

Default mode

fourcv

omega, chi, phi, ttheta

6

bisecting

fourch

omega, chi, phi, ttheta

6

bisecting

psic

mu, eta, chi, phi, nu, delta

24

bisecting_vertical

sixc

alpha, omega, chi, phi, delta, gamma

6

bisecting_4c

fivec

mu, omega, chi, phi, ttheta

5

bisecting_4c

kappa4cv

komega, kappa, kphi, ttheta

7

bisecting

kappa4ch

komega, kappa, kphi, ttheta

6

bisecting

kappa6c

mu, komega, kappa, kphi, nu, delta

14

bisecting_vertical

zaxis

alpha, Z, delta, gamma

2

zaxis

s2d2

mu, Z, nu, delta

2

fixed_mu

Register a custom YAML geometry#

Since ad_hoc_diffractometer 0.10.0 (issue #267), geometries are described in declarative YAML files. You can extend the ad_hoc solver with your own geometry by registering a YAML file before creating the diffractometer. The AdHocSolver discovers geometries dynamically from the library’s registry, so no wrapper change is required.

import ad_hoc_diffractometer as ahd
import hklpy2

# Register a YAML geometry from disk under the name 'mybeamline'.
ahd.register_geometry_file("/path/to/mybeamline.yml", name="mybeamline")

# Or load and inspect without registering:
geom = ahd.load_geometry_file("/path/to/mybeamline.yml")

# The new geometry is now discoverable through the ad_hoc solver.
diff = hklpy2.creator(
    solver="ad_hoc",
    geometry="mybeamline",
    name="mybeamline",
)

The name argument is optional; when omitted, the geometry is registered under the name: field declared inside the YAML file. See the ad_hoc_diffractometer schema for the YAML format.

Third-party packages can alternatively contribute geometries via the "ad_hoc_diffractometer.geometries" entry-point group; those are discovered automatically the first time ad_hoc_diffractometer.list_geometries() is called.

See also