Module: geometries#

See examples in the Diffractometer Examples section.

Source code documentation#

diffract#

Support for diffractometer instances.

SUPPORT

SimMixin([prefix, kind, read_attrs, ...])

Defines h, k, & l pseudo-positioners.

DIFFRACTOMETER GEOMETRIES

E4CH(prefix[, calc_kw, decision_fcn, ...])

Eulerian 4-circle, horizontal scattering plane

E4CV(prefix[, calc_kw, decision_fcn, ...])

Eulerian 4-circle, vertical scattering plane

E6C(prefix[, calc_kw, decision_fcn, ...])

Eulerian 6-circle, vertical scattering plane

K4CV(prefix[, calc_kw, decision_fcn, ...])

Kappa 4-circle, vertical scattering plane

K6C(prefix[, calc_kw, decision_fcn, ...])

Kappa 6-circle, vertical scattering plane

Zaxis(prefix[, calc_kw, decision_fcn, ...])

Z-axis geometry

SimulatedE4CV(prefix[, calc_kw, ...])

SimulatedE4CV: Eulerian 4-circle diffractometer, vertical

SimulatedE6C(prefix[, calc_kw, ...])

SimulatedE6C: Eulerian 6-circle diffractometer

SimulatedK4CV(prefix[, calc_kw, ...])

SimulatedK4CV: Kappa 4-circle diffractometer, vertical

SimulatedK6C(prefix[, calc_kw, ...])

SimulatedK6C: Kappa 6-circle diffractometer

SPECIAL-USE DIFFRACTOMETER GEOMETRIES

ApsPolar(prefix[, calc_kw, decision_fcn, ...])

APS POLAR 6-circle diffractometer.

Petra3_p09_eh2(prefix[, calc_kw, ...])

6-circle Used at Petra3 P09

Petra3_p23_4c(prefix[, calc_kw, ...])

4-circle Used at Petra3 P23

Petra3_p23_6c(prefix[, calc_kw, ...])

7-circle Used at Petra3 P23

SoleilMars(prefix[, calc_kw, decision_fcn, ...])

Used at Soleil

SoleilNanoscopiumRobot(prefix[, calc_kw, ...])

Used at Soleil

SoleilSiriusKappa(prefix[, calc_kw, ...])

Used at Soleil

SoleilSiriusTurret(prefix[, calc_kw, ...])

Used at Soleil

SoleilSixsMed1p2(prefix[, calc_kw, ...])

Used at Soleil

SoleilSixsMed2p2(prefix[, calc_kw, ...])

Used at Soleil

SoleilSixsMed2p3(prefix[, calc_kw, ...])

Used at Soleil

SoleilSixsMed2p3v2(prefix[, calc_kw, ...])

Used at Soleil

class hkl.geometries.ApsPolar(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

APS POLAR 6-circle diffractometer.

class hkl.geometries.E4CH(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Eulerian 4-circle, horizontal scattering plane

class hkl.geometries.E4CV(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Eulerian 4-circle, vertical scattering plane

class hkl.geometries.E6C(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Eulerian 6-circle, vertical scattering plane

class hkl.geometries.K4CV(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Kappa 4-circle, vertical scattering plane

class hkl.geometries.K6C(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Kappa 6-circle, vertical scattering plane

class hkl.geometries.Petra3_p09_eh2(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

6-circle Used at Petra3 P09

class hkl.geometries.Petra3_p23_4c(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

4-circle Used at Petra3 P23

class hkl.geometries.Petra3_p23_6c(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

7-circle Used at Petra3 P23

class hkl.geometries.SimMixin(prefix='', *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs)[source]#

Defines h, k, & l pseudo-positioners.

Use this mixin class with any of the diffractometer geometries to create your own simulator. Follow one of the simulators below, such as SimulatedE4CV. You should replace E4CV with your geomtry’s name. And, you will need to create SoftPositioner components for each of the real-space axes, in the order required by that geometry.

class hkl.geometries.SimulatedE4CV(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

SimulatedE4CV: Eulerian 4-circle diffractometer, vertical

class hkl.geometries.SimulatedE6C(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

SimulatedE6C: Eulerian 6-circle diffractometer

class hkl.geometries.SimulatedK4CV(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

SimulatedK4CV: Kappa 4-circle diffractometer, vertical

class hkl.geometries.SimulatedK6C(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

SimulatedK6C: Kappa 6-circle diffractometer

class hkl.geometries.SoleilMars(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilNanoscopiumRobot(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilSiriusKappa(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilSiriusTurret(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilSixsMed1p2(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilSixsMed2p2(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilSixsMed2p3(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.SoleilSixsMed2p3v2(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Used at Soleil

class hkl.geometries.Zaxis(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Z-axis geometry


Examples#

Demonstrate the setup of diffractometers using the hkl package.

The Module: sample Examples section describes how to setup a crystal sample with an orientation matrix.

sim6c: Eulerian 6-circle with simulated motors#

It is useful, sometimes, to create a simulated diffractometer where the motor axes are provided by software simulations, [1] rather than using the actual motors provided by the diffractometer.

Load the simulated 6-circle geometry from hklpy:

1from hkl import SimulatedE6C

Create an instance of this diffractometer with:

sim6c = SimulatedE6C("", name="sim6c")

k4cv : kappa 4-circle with EPICS motor PVs#

To control a kappa diffractometer (in 4-circle geometry with vertical scattering plane) where the motor axes are provided by EPICS PVs, use ophyd.EpicsMotor. [2]

In this example, we know from our local control system that the kappa motors have these PVs:

kappa axis

EPICS PVs

komega

sky:m1

kappa

sky:m2

kphi

sky:m3

tth

sky:m4

In this case, we must first create a custom kappa 4-circle subclass and connect our motor PVs to the positioners for the real axes:

 1import hkl
 2from ophyd import Component
 3from ophyd import EpicsMotor
 4from ophyd import PseudoSingle
 5
 6class KappaK4CV(hkl.K4CV):
 7    """K4CV: kappa diffractometer in 4-circle geometry"""
 8
 9    h = Component(PseudoSingle, "")
10    k = Component(PseudoSingle, "")
11    l = Component(PseudoSingle, "")
12
13    komega = Component(EpicsMotor, "sky:m1")
14    kappa = Component(EpicsMotor, "sky:m2")
15    kphi = Component(EpicsMotor, "sky:m3")
16    tth = Component(EpicsMotor, "sky:m4")

Create an instance of this diffractometer with:

k4cv = KappaK4CV("", name="k4cv")

k4cve : k4cv with energy from local control system#

Extend the k4cv example above to use the energy as provided by the local control system. In this example, assume these are the PVs to be used:

signal

EPICS PV

energy

optics:energy

energy units

optics:energy.EGU

energy locked?

optics:energy_locked

energy offset

no PV, same units as energy (above)

The energy locked? signal is a flag controlled by the user that controls whether (or not) the energy signal will update the wavelength of the diffractometer’s calc engine. We expect this to be either 1 (update the calc engine) or 0 (do NOT update the calc engine).

We’ll also create a (non-EPICS) signal to provide for an energy offset (in the same units as the control system energy). This offset will be added to the control system energy (in _update_calc_energy()), before conversion of the units to keV and then setting the diffractometer’s calc engine energy:

calc engine energy (keV) = in_keV(control system energy + offset)

which then sets the wavelength:

calc engine wavelength (angstrom) = \(h\nu\) / calc engine energy

(\(h\nu=\) 12.39842 angstrom \(\cdot\) keV) and account for the units of the control system energy. To combine all this, we define a new python class starting similar to KappaK4CV above, and adding the energy signals. Create the custom kappa 4-circle subclass with energy:

 1import hkl
 2from ophyd import Component
 3from ophyd import PseudoSingle
 4from ophyd import EpicsMotor
 5from ophyd import EpicsSignal
 6from ophyd import Signal
 7import pint
 8
 9class KappaK4CV_Energy(hkl.K4CV):
10    """
11    K4CV: kappa diffractometer in 4-circle geometry with energy
12    """
13
14    h = Component(PseudoSingle, "")
15    k = Component(PseudoSingle, "")
16    l = Component(PseudoSingle, "")
17
18    komega = Component(EpicsMotor, "sky:m1")
19    kappa = Component(EpicsMotor, "sky:m2")
20    kphi = Component(EpicsMotor, "sky:m3")
21    tth = Component(EpicsMotor, "sky:m4")
22
23    energy = Component(EpicsSignal, "optics:energy")
24    energy_units = Component(EpicsSignal, "optics:energy.EGU")
25    energy_offset = Component(Signal, value=0)
26    energy_update_calc_flag = Component(
27        EpicsSignal,
28        "optics:energy_locked")

Create an instance of this diffractometer with:

k4cve = KappaK4CV_Energy("", name="k4cve")

Note

If you get a log message such as this on the console:

W Fri-09:12:16 - k4cve not fully connected, k4cve.calc.energy not updated

this informs you the update cannot happen until all EPICS PVs are connected. The following code, will create the object, wait for all PVs to connect, then update the calc engine:

k4cve = KappaK4CV_Energy("", name="k4cve")
k4cve.wait_for_connection()
k4cve._energy_changed(k4cve.energy.get())

To set the energy offset from the command line:

%mov k4cve.energy_offset 50

which means the diffractometer (assuming the control system uses “eV” units) will use an energy that is 50 eV higher than the control system reports. The diffractometer’s calc engine will only be updated when the energy signal is next updated. To force an update to the calc engine, call _energy_changed() directly with the energy value as the argument:

k4cve._energy_changed()

But this only works when the optics:energy_locked PV is 1 (permitted to update the calc engine energy). To update the diffractometer’s calc engine energy and bypass the k4cve.energy_update_calc_flag signal, call this command:

k4cve._update_calc_energy()

Finally, to set the energy of the diffractometer’s calc engine, use one of these two methods:

energy_update_calc_flag

command

obey signal

k4cve._energy_changed()

ignore signal

k4cve._update_calc_energy()

Each of these two methods will accept an optional value argument which, if provided, will be used in place of the energy signal from the control system.