Constraints#

Overview#

Computation of the real-space axis positions given a set of reciprocal-space coordinates can have many solutions. One or more constraints (Constraint) (a.k.a, cut points), together with a choice of operating mode, can be applied to:

  • limit the range of forward() solutions accepted for that positioner

  • declare the value to use when the positioner should be kept constant

Diffractometer constraints are described by:

Constraint(low_limit, high_limit, value[, fit])

Limitations on acceptable positions from computed forward() solutions.

These functions manage constraints:

apply_constraints(constraints)

Constrain the solutions of the diffractometer's forward() computation.

reset_constraints()

Set constraints back to initial settings.

show_constraints([fmt, printing])

Print the current constraints in a table.

undo_last_constraints()

Remove the current constraints additions, restore previous.

restore_constraints(orientation, diffractometer)

Restore any constraints from orientation information.

Diffractometer constraints can be exported and restored as part of a DiffractometerConfiguration:

export([fmt])

Export configuration in a recognized format (dict, JSON, YAML, file).

restore(data[, clear, restore_constraints])

Restore configuration from a recognized format (dict, JSON, YAML, file).

Tip

Constraints are implemented as cut points in other software. Similar yet not identical.

Create a constraint#

A Constraint is defined as a Constraint object. For convenience, import directly from hkl.Constraint:

hkl.Constraint(0, 90, 0, True)

Consider a diffractometer object:

from hkl import SimulatedE4CV
e4cv = SimulatedE4CV("", name="e4cv")

The default constraints for this geometry are (using e4cv.show_constraints()):

axis

low_limit

high_limit

value

fit

omega

-180.0

180.0

0.0

True

chi

-180.0

180.0

0.0

True

phi

-180.0

180.0

0.0

True

tth

-180.0

180.0

0.0

True

Apply the constraint#

Apply a constraint just to the “tth” axis such that \(0<=2\theta<=120\):

e4cv.apply_constraints({"tth": Constraint(0, 120, 0, True)})

Show the constraints now (again, using e4cv.show_constraints()):

axis

low_limit

high_limit

value

fit

omega

-180.0

180.0

0.0

True

chi

-180.0

180.0

0.0

True

phi

-180.0

180.0

0.0

True

tth

0.0

119.99999999999999

0.0

True

Remove the constraint#

Remove the previous constraint:

e4cv.undo_last_constraints()
e4cv.show_constraints()

Back to the defaults again:

axis

low_limit

high_limit

value

fit

omega

-180.0

180.0

0.0

True

chi

-180.0

180.0

0.0

True

phi

-180.0

180.0

0.0

True

tth

-180.0

180.0

0.0

True

Example#

Using the default sample (main), show the possible forward() solutions for a \((100)\) position with a hkl.geometries.E4CV (4-circle) geometry diffractometer with these constraints:

axis

low_limit

high_limit

value

fit

omega

10.0

40.0

0.0

True

chi

-100.0

100.0

0.0

True

phi

-100.0

100.0

0.0

True

tth

10.0

92.4

0.0

True

First, make the diffractometer (simulator) and show the default constraints:

1from hkl import SimulatedE4CV
2
3e4cv = SimulatedE4CV("", name="e4cv")
4e4cv.show_constraints()

axis

low_limit

high_limit

value

fit

omega

-180.0

180.0

0.0

True

chi

-180.0

180.0

0.0

True

phi

-180.0

180.0

0.0

True

tth

-180.0

180.0

0.0

True

Make a convenience function to show all the possible forward() solutions in a table. The complete list of possible solutions is provided by the low-level forward() method:

 1import pyRestTable
 2
 3def all_forward_solutions(hkl_position):
 4    axes = e4cv.calc.physical_axis_names
 5    table = pyRestTable.Table()
 6    table.labels = axes
 7    for sol in e4cv.calc.forward(hkl_position):
 8        table.addRow([round(getattr(sol, k), 2) for k in axes])
 9    print(f"solutions for forward({hkl_position}):")
10    print(table)

Show all solutions for the \((100)\) position (note the inner set of parentheses):

all_forward_solutions((1, 0, 0))

solutions for forward((1, 0, 0)):

omega

chi

phi

tth

-30.21

0.0

-90.0

-60.42

30.21

0.0

90.0

60.42

-149.79

0.0

29.58

-60.42

-30.21

0.0

150.42

60.42

30.21

0.0

-150.42

-60.42

-149.79

0.0

-90.0

60.42

-30.21

180.0

90.0

-60.42

30.21

180.0

-90.0

60.42

-149.79

180.0

-29.58

-60.42

-30.21

180.0

-150.42

60.42

30.21

180.0

150.42

-60.42

-149.79

180.0

90.0

60.42

Next, apply the new constraints and print the revised table:

1e4cv.apply_constraints(
2    {
3        "omega": Constraint(10, 40, 0, True),
4        "chi": Constraint(-100, 100, 0, True),
5        "phi": Constraint(-100, 100, 0, True),
6        "tth": Constraint(10, 92.4, 0, True),
7    }
8)
9all_forward_solutions((1, 0, 0))

solutions for forward((1, 0, 0)):

omega

chi

phi

tth

30.21

0.0

90.0

60.42

Only one solution satisfies these constraints.

forward()#

Given a set of reciprocal-space coordinates (typically \(h\), \(k\), and \(l\)), compute the different sets of real-space coordinates which match. In the general case, the problem is over-determined. Multiple solutions are expected. These are the forward() computation methods:

The hkl.diffract.Diffractometer.forward() method selects the first allowed solution from hkl.calc.CalcRecip.forward(). This is the default choice as defined by hkl.calc.default_decision_function(). You can replace it with your own function. Then, either:

  • (easier) set your diffractometer object’s _decision_fcn attribute, such as: e4cv._decision_fcn=your_function

  • (harder) pass it via the decision_fcn=your_function keyword when creating the Diffractometer object