Switch Diffraction Modes#
A diffraction mode is a ConstraintSet
that describes which motor stages are free, which are held at declared values,
and which are related to others (e.g. bisecting).
See Concepts for background, and
Work with Constraints and Diffraction Modes for the full constraint framework.
List available modes#
import ad_hoc_diffractometer as ahd
g = ahd.make_geometry("fourcv")
print(list(g.modes.keys()))
# ['bisecting', 'fixed_chi', 'fixed_phi', 'fixed_omega',
# 'fixed_psi', 'double_diffraction']
Get and set the active mode#
# The default mode is set by the demo geometry
print(g.mode_name) # 'bisecting'
# Switch to a different mode
g.mode_name = "fixed_chi"
print(g.mode_name) # 'fixed_chi'
# Clear the active mode (all stages free — forward() will raise)
g.mode_name = None
Mode reference#
Bisecting mode#
The bisecting BisectConstraint drives the
co-axial sample stage to half the detector angle, placing the sample
symmetrically between the incident and diffracted beams.
g.mode_name = "bisecting"
solutions = g.forward(1, 0, 0)
# omega = ttheta / 2 in every solution
Fixed-angle modes#
Fixed-angle modes hold one sample stage at its declared constraint value
during a single forward() call. The value is part of the ConstraintSet
definition and is constant only for the duration of that call.
# fixed_chi: demo geometry holds chi at 90°
g.mode_name = "fixed_chi"
solutions = g.forward(1, 0, 0)
# sol["chi"] == 90.0 for every solution
To use a different value, the simplest call is
with_constraint_values(),
which returns a fresh ConstraintSet with the named constraint values
replaced:
# Single value: chi = 45° on the demo fixed_chi mode
g.modes["fixed_chi"] = g.modes["fixed_chi"].with_constraint_values(chi=45.0)
g.mode_name = "fixed_chi"
sols_45 = g.forward(1, 0, 0) # chi = 45°
# Several values at once (e.g. psic B3 mode: two sample stages plus
# the incidence target — three pinned values in one call):
g.modes["fixed_incidence_fixed_chi_fixed_phi"] = (
g.modes["fixed_incidence_fixed_chi_fixed_phi"]
.with_constraint_values(chi=15.0, phi=30.0, incidence=5.0)
)
For modes where you want to change which constraints appear (not just
their values), build a new ConstraintSet
directly:
from ad_hoc_diffractometer import ConstraintSet, SampleConstraint
g.modes["my_chi"] = ConstraintSet([SampleConstraint("chi", 60.0)])
g.mode_name = "my_chi"
See Work with Constraints and Diffraction Modes for the full run-time pattern, including how to override detector and reference-target values and the rationale for the immutable-constraint design.
Change a fixed-axis value#
Yes, you can override the YAML default for any fixed_* mode at run
time. Use
with_constraint_values()
for a one-call value swap (single or multiple values), or rebuild the
ConstraintSet from scratch when
you also need to change which constraints appear. Worked examples for
sample, detector, and reference-target overrides are in
Work with Constraints and Diffraction Modes.
fixed_omega#
Holds omega at 0° (any geometry where omega is a sample stage).
g.mode_name = "fixed_omega"
solutions = g.forward(1, 0, 0)
# sol["omega"] == 0.0 for every solution
Inspect a mode’s constraints#
g = ahd.make_geometry("fourcv")
cs = g.modes["fixed_chi"]
print(cs)
# ConstraintSet([SampleConstraint('chi', 90.0)])
print(cs.computed)
# ['omega', 'phi', 'ttheta']
print(cs.constant_stages)
# ['chi']
print(cs.is_fully_constrained(g))
# True (N - 3 = 1 constraint for N = 4 DOF)
print(cs.is_implemented(g))
# True
Check if a mode is implemented#
Some modes require a prerequisite on the geometry. For example,
fixed_psi requires g.azimuth to be set:
g.mode_name = "fixed_psi"
print(g.modes["fixed_psi"].is_implemented(g)) # False (no azimuth)
g.azimuth = (0, 0, 1)
print(g.modes["fixed_psi"].is_implemented(g)) # True
# forward() raises NotImplementedError for unimplemented modes
Clear the active mode#
g.mode_name = None # all stages free; forward() raises NotImplementedError
See also#
Work with Constraints and Diffraction Modes — full constraint framework: DOF rule, custom modes, extras