Quick Start#
This guide walks through the steps to build a four-circle diffractometer by hand — without using a demo geometry. Building it step by step makes the design choices explicit and shows how every diffractometer in the package is constructed. This builds a diffractometer for the vertical scattering plane.
The concise demo geometry equivalent appears at the end.
Tip
If you already have the package installed and just want a five-line example that uses one of the demo geometries, see the Demonstration Geometries Quick start page instead.
1. Choose a coordinate basis#
Any right-handed orthogonal mapping of the three physical directions (vertical, longitudinal, transverse) to Cartesian unit vectors (x, y, z) is a valid basis. Two conventions (shown below) are used in the demo examples. You may supply any basis (dictionary) that satisfies those constraints:
See Choose and Understand Basis Vectors for a full tutorial on choosing and understanding basis vectors.
For a standard four-circle diffractometer (Busing & Levy 1967) choose
BASIS_BL:
import numpy as np
import ad_hoc_diffractometer as ahd
BASIS = ahd.BASIS_BL
TRANSVERSE = BASIS["transverse"] # +x
LONGITUDINAL = BASIS["longitudinal"] # +y
VERTICAL = BASIS["vertical"] # +z
2. Define the stage stack#
A four-circle diffractometer has three sample stages and one detector stage.
Each Stage is described by:
name — motor name used in angle dictionaries and
forward()resultsaxis — rotation axis vector; a leading
−means left-handed rotationparent — the stage this one sits on (
None= directly on the floor)role —
"sample"or"detector"
The fourcv (vertical scattering plane, synchrotron) stack is:
stages = [
# Sample stack — base stage first
ahd.Stage("omega", -TRANSVERSE, parent=None, role="sample"),
ahd.Stage("chi", +LONGITUDINAL, parent="omega", role="sample"),
ahd.Stage("phi", -TRANSVERSE, parent="chi", role="sample"),
# Detector — independent of the sample stack
ahd.Stage("ttheta", -TRANSVERSE, parent=None, role="detector"),
]
omega and ttheta both rotate about the transverse axis, so their scattering
plane is vertical (the synchrotron convention). For the laboratory
(horizontal scattering plane) convention swap every TRANSVERSE for VERTICAL
and every LONGITUDINAL for TRANSVERSE — that is the fourch — Eulerian Four-Circle (horizontal) geometry.
3. Define diffraction modes#
A ConstraintSet specifies which degrees of
freedom are constrained during a forward (hkl → motor angles) calculation.
Three constraint types cover most cases:
BisectConstraint— ties a sample stage to half the detector angle, placing the sample symmetrically in the beam.SampleConstraint— holds one sample stage at a fixed angle.DetectorConstraint— holds one detector stage at a fixed angle.
modes = {
"bisecting": ahd.ConstraintSet(
[ahd.BisectConstraint("omega", "ttheta")],
computed=["omega", "chi", "phi", "ttheta"],
),
"fixed_chi": ahd.ConstraintSet(
[ahd.SampleConstraint("chi", 90.0)],
computed=["omega", "phi", "ttheta"],
),
"fixed_phi": ahd.ConstraintSet(
[ahd.SampleConstraint("phi", 0.0)],
computed=["omega", "chi", "ttheta"],
),
}
4. Assemble the geometry#
Pass the stage list, basis, and modes to
AdHocDiffractometer:
g = ahd.AdHocDiffractometer(
name="my_fourcv",
stages=stages,
basis=BASIS,
description="Four-circle Eulerian, vertical scattering plane",
modes=modes,
default_mode="bisecting",
)
5. Set the wavelength and sample lattice#
g.wavelength = 1.5406 # Å (Cu Kα)
g.sample.lattice = ahd.Lattice(a=5.431) # cubic silicon
6. Inspect the geometry#
print(g.summary())
Example output:
Geometry: my_fourcv
Four-circle Eulerian, vertical scattering plane
Wavelength: 1.5406 Å Energy: 8.0478 keV
Mode: bisecting
Sample stages:
omega axis=-transverse angle= 0.000° limits=(-180.0, 180.0)
chi axis=+longitudinal angle= 0.000° limits=(-180.0, 180.0)
phi axis=-transverse angle= 0.000° limits=(-180.0, 180.0)
Detector stages:
ttheta axis=-transverse angle= 0.000° limits=(-180.0, 180.0)
7. Solve the forward problem#
Given a reflection (hkl), find the motor angles that satisfy Bragg’s law.
First orient the crystal (see Orient a Crystal), then call forward():
# Minimal orientation: identity U matrix (crystal axes || diffractometer axes)
ahd.ub_identity(g.sample)
# Solve for the (0, 0, 4) reflection
solutions = g.forward(0, 0, 4)
for sol in solutions:
print(sol)
Concise form — using a demo geometry#
The code above is exactly what the built-in fourcv() demo geometry does.
If you do not need to customize anything, use it directly:
import ad_hoc_diffractometer as ahd
g = ahd.make_geometry("fourcv") # Busing & Levy (1967) four-circle, vertical plane
g.wavelength = 1.5406 # Å
g.sample.lattice = ahd.Lattice(a=5.431)
print(g.summary())
See fourcv — Eulerian Four-Circle (vertical) for the full geometry reference, or fourch — Eulerian Four-Circle (horizontal) for the horizontal-plane (laboratory) variant.
See also#
Demonstration Geometries Quick start — the five-line demo geometry example
Build a Custom Diffractometer Geometry — how to build a diffractometer that is not one of the demos
Choose and Understand Basis Vectors — choosing and understanding basis vectors