(geometry-kappa4ch)=
# kappa4ch — Kappa Four-Circle (Laboratory)
Four-circle kappa diffractometer, horizontal scattering plane. Kappa
axis tilted at α = 50° **from the outer komega axis (which here is
along the −vertical direction) toward the equivalent Eulerian chi
axis**. Laboratory convention.
**Walko (2016) designation:** S3D1 (kappa)
**Coordinate basis:** Busing & Levy ({data}`~ad_hoc_diffractometer.factories.BASIS_BL`): transverse=+x, longitudinal=+y, vertical=+z.
## Quick start
```python
import ad_hoc_diffractometer as ahd
g = ahd.presets.kappa4ch()
g.wavelength = 1.0 # Å
print(g.summary())
```
## Pre-built geometry definition
This geometry is defined by the {func}`~ad_hoc_diffractometer.presets.kappa4ch` factory
function — see the [source](https://github.com/prjemian/ad_hoc_diffractometer/blob/main/src/ad_hoc_diffractometer/factories.py#L964) for the complete stage
and mode configuration.
## Stage layout
```{raw} html
Static fallback (click to expand if the interactive figure above is blank)
```

```{raw} html
```
**Sample stages (base first):**
| Stage | Axis | Handedness | Parent |
|---|---|---|---|
| ``komega`` | −vertical (−z BL) | left-handed | base |
| ``kappa`` | −z · cos α + ŷ · sin α (α = 50°) | right-handed | ``komega`` |
| ``kphi`` | −vertical (−z BL) | left-handed | ``kappa`` |
**Detector stages (base first):**
| Stage | Axis | Handedness | Parent |
|---|---|---|---|
| ``ttheta`` | −vertical (−z BL) | left-handed | base |
The kappa axis is computed by
{func}`~ad_hoc_diffractometer.kappa.kappa_axis_from_eulerian` from the
preset's actual ``komega`` and equivalent Eulerian chi axes:
$$
\hat{n}_{\kappa} \;=\; \cos\alpha \cdot \hat{n}_{\kappa\omega} \;+\; \sin\alpha \cdot \hat{n}_{\chi,\,\text{eq}}
\;=\; \cos 50° \cdot (-\hat{z}) \;+\; \sin 50° \cdot (+\hat{y}).
$$
This formulation is geometry-aware and is correct for the
``kappa4ch`` ``komega = -VERTICAL`` orientation. See the
[kappa4cv documentation](kappa4cv-axis-definition) and issue #241
for the reasons this differs from the textbook
``vertical · cos α + transverse · sin α`` formula.
**Virtual Eulerian angles** ``omega``, ``chi``, ``phi`` are mapped
to / from the real motors via the geometry-aware decomposition in
{func}`~ad_hoc_diffractometer.kappa.eulerian_to_kappa_axes` and
{func}`~ad_hoc_diffractometer.kappa.kappa_to_eulerian_axes`.
## Diffraction modes
Each mode is a {class}`~ad_hoc_diffractometer.mode.ConstraintSet` of 1 constraint
(N − 3 = 1 for N = 4 DOF).
Identical mode set to {doc}`kappa4cv`.
See {doc}`../howto/modes` for usage and {doc}`../howto/constraints` for
changing constraint values at run time.
### `bisecting` *(default)*
{class}`~ad_hoc_diffractometer.mode.VirtualBisectConstraint`:
``omega_virtual = ttheta / 2`` enforced on the virtual Eulerian
omega pseudoangle. Solved via the geometry-aware
{func}`~ad_hoc_diffractometer.kappa.eulerian_to_kappa_axes`
decomposition (issue #241).
| | |
|---|---|
| **Computed** | komega, kappa, kphi, ttheta |
| **Constant during** `forward()` | — |
### `fixed_kphi`
{class}`~ad_hoc_diffractometer.mode.SampleConstraint`:
`kphi` held at declared value (default 0°) — real stage, no kappa inversion needed.
| | |
|---|---|
| **Computed** | komega, kappa, ttheta |
| **Constant during** `forward()` | kphi |
### `fixed_omega`
Fix virtual Eulerian omega at declared value (default 0°) — see {doc}`kappa4cv` for details.
| | |
|---|---|
| **Computed** | komega, kappa, kphi, ttheta |
| **Constant during** `forward()` | omega (virtual) |
### `fixed_chi`
Fix virtual Eulerian chi at declared value (default 90°).
| | |
|---|---|
| **Computed** | komega, kappa, kphi, ttheta |
| **Constant during** `forward()` | chi (virtual) |
### `fixed_phi`
Fix virtual Eulerian phi at declared value (default 0°).
| | |
|---|---|
| **Computed** | komega, kappa, kphi, ttheta |
| **Constant during** `forward()` | phi (virtual) |
### `fixed_psi`
{class}`~ad_hoc_diffractometer.mode.ReferenceConstraint`:
azimuthal angle ψ validation filter.
Set ``g.azimuthal_reference = (h, k, l)`` before calling ``forward()``.
Returns bisecting solutions only when the natural ψ for (h,k,l) matches
the stored target. See {doc}`../howto/surface`.
| | |
|---|---|
| **Extras (input)** | n̂ (reference vector), ψ (target azimuth, degrees) |
| **Extras (output)** | psi (computed azimuth) |
## API reference
- {func}`~ad_hoc_diffractometer.presets.kappa4ch`
- {class}`~ad_hoc_diffractometer.diffractometer.AdHocDiffractometer`
- {class}`~ad_hoc_diffractometer.mode.ConstraintSet`
- {class}`~ad_hoc_diffractometer.mode.BisectConstraint`
- {class}`~ad_hoc_diffractometer.mode.SampleConstraint`
- {class}`~ad_hoc_diffractometer.mode.ReferenceConstraint`
- {class}`~ad_hoc_diffractometer.mode.EwaldSphereViolation`
- {class}`~ad_hoc_diffractometer.mode.ConstraintViolation`
## References
- ITC Vol. C §2.2.6 (2006). DOI: [10.1107/97809553602060000577](https://doi.org/10.1107/97809553602060000577)
- Walko, *Ref. Module Mater. Sci. Mater. Eng.* (2016), eq. [16].