factories#
Import: ad_hoc_diffractometer.factories
This module provides the geometry registry infrastructure: the
@register_geometry decorator, discovery of third-party geometry plugins
via entry points, and the list_geometries() / get_geometry() /
make_geometry() lookup functions.
It also defines the shared basis dictionaries (BASIS_YOU,
BASIS_BL, BASIS_DEFAULT) and the default kappa tilt angle
(KAPPA_ALPHA_DEFAULT) used by the demo geometries shipped under
ad_hoc_diffractometer.geometries.
Demo geometries#
The 10 demo geometries (psic, fourcv, fourch, sixc,
kappa4cv, kappa4ch, kappa6c, zaxis, s2d2,
fivec) are declarative YAML files in
ad_hoc_diffractometer.geometries. Access them via the
registry:
import ad_hoc_diffractometer as ahd
g = ahd.make_geometry("fourcv")
Extension via entry points#
Third-party packages can contribute additional geometries by
declaring an entry point in the "ad_hoc_diffractometer.geometries"
group in their pyproject.toml:
[project.entry-points."ad_hoc_diffractometer.geometries"]
my_geom = "my_package.module:my_geometry_function"
Each geometry function must accept no required arguments (it may accept
keyword arguments) and return an AdHocDiffractometer instance.
Entry-point geometries are discovered and loaded automatically when
list_geometries() or get_geometry() is first called; they do NOT
need to call @register_geometry themselves.
Geometry names must be globally unique. If an entry-point name
duplicates an already-registered name (whether a built-in or a
previously loaded plugin), a ValueError is raised at discovery time.
This prevents silent shadowing: an external package cannot overwrite
fourcv, psic, or any other registered geometry.
Writing a custom geometry#
Each geometry function accepts an optional basis keyword argument
(defaulting to the canonical convention for that geometry). Inside the
function, resolve physical-direction aliases locally:
from ad_hoc_diffractometer import AdHocDiffractometer, register_geometry
from ad_hoc_diffractometer.factories import BASIS_YOU
from ad_hoc_diffractometer.stage import Stage
@register_geometry
def my_geometry(basis=BASIS_YOU):
VERTICAL = basis["vertical"]
TRANSVERSE = basis["transverse"]
LONGITUDINAL = basis["longitudinal"]
stages = [
Stage("omega", -TRANSVERSE, role="sample"),
Stage("chi", +LONGITUDINAL, parent="omega", role="sample"),
Stage("phi", -TRANSVERSE, parent="chi", role="sample"),
Stage("ttheta", -TRANSVERSE, role="detector"),
]
return AdHocDiffractometer(
name="my_geometry", stages=stages, basis=basis,
)
The two public basis dicts BASIS_YOU and BASIS_BL are available
from this module. Pass basis=BASIS_BL for Busing & Levy convention
geometries.
References
W.R. Busing & H.A. Levy, Acta Cryst. 22, 457-464 (1967)
H. You, J. Appl. Cryst. 32, 614-623 (1999) DOI:10.1107/S0021889899001223
ITC Vol. C, Sec. 2.2.6 (2006) DOI:10.1107/97809553602060000577
D.A. Walko, Ref. Module Mater. Sci. Mater. Eng. (2016)
Attributes#
Basis vector dictionary for the Busing & Levy (1967) coordinate convention. |
|
Neutral basis used by the declarative geometry loader when a YAML file |
|
Basis vector dictionary for the You (1999) coordinate convention. |
|
Entry-point group name for geometry plugins. |
|
Default kappa tilt angle in degrees (Walko 2016; Enraf-Nonius; ITC Vol. C). |
Functions#
|
Discover and load geometry callables from installed entry points. |
|
Return the registered geometry callable by name. |
|
Return a copy of the geometry registry as {name: callable}. |
|
Instantiate a geometry by name, passing keyword arguments to its |
|
Decorator that registers a geometry callable in _GEOMETRY_REGISTRY. |
Module Contents#
- ad_hoc_diffractometer.factories.BASIS_BL#
Basis vector dictionary for the Busing & Levy (1967) coordinate convention.
Maps physical direction names to Cartesian unit vectors:
"transverse"→ +x"longitudinal"→ +y (along the beam)"vertical"→ +z (opposite to gravitational acceleration)
Used by
fourcv,fourch,kappa4cv, andkappa4ch(inad_hoc_diffractometer.geometries).
- ad_hoc_diffractometer.factories.BASIS_DEFAULT#
Neutral basis used by the declarative geometry loader when a YAML file omits the
basis:key (issue #267).Maps physical direction names to Cartesian unit vectors:
"vertical"→YHAT(+y)"longitudinal"→ZHAT(+z)"transverse"→XHAT(+x)
This basis is deliberately distinct from both
BASIS_YOU(You 1999) andBASIS_BL(Busing & Levy 1967) so that the package does not appear to espouse either literature convention as a “project default.” YAML files that should match a literature convention must declare the basis explicitly (basis: BL,basis: YOU, or an explicit mapping); files that omit the key opt into this neutral fallback.The Cartesian basis
(XHAT, YHAT, ZHAT)is right-handed; the mapping above is one of three possible cyclic permutations of physical names onto those axes. Compare:
- ad_hoc_diffractometer.factories.BASIS_YOU#
Basis vector dictionary for the You (1999) coordinate convention.
Maps physical direction names to Cartesian unit vectors:
"vertical"→XHAT(+x, opposite to gravitational acceleration)"longitudinal"→YHAT(+y, along the beam)"transverse"→ZHAT(+z, completes the right-handed system: vertical × longitudinal)
Default basis used by
psic,sixc,kappa6c,zaxis,s2d2, andfivec(inad_hoc_diffractometer.geometries).
- ad_hoc_diffractometer.factories.GEOMETRY_ENTRY_POINT_GROUP = 'ad_hoc_diffractometer.geometries'#
Entry-point group name for geometry plugins.
- ad_hoc_diffractometer.factories.KAPPA_ALPHA_DEFAULT = 50.0#
Default kappa tilt angle in degrees (Walko 2016; Enraf-Nonius; ITC Vol. C).
- ad_hoc_diffractometer.factories._load_entry_point_geometries() None[source]#
Import:
ad_hoc_diffractometer.factories._load_entry_point_geometriesDiscover and load geometry callables from installed entry points.
Scans the
"ad_hoc_diffractometer.geometries"entry-point group for all installed packages (including this package itself) and adds any geometry callables not already present in_GEOMETRY_REGISTRY.This function is called automatically — and only once — by
list_geometries()andget_geometry(). It is idempotent: repeated calls after the first are no-ops.Notes
Built-in demo geometries are registered via
@register_geometryat import time, so they are always present even if entry-point discovery fails. Entry-point discovery supplements the registry with any third-party plugins that are installed but were not decorated with@register_geometry.Each geometry name must be unique across all installed packages. If an entry-point name collides with an already-registered name (whether a built-in or a previously loaded plugin), a
ValueErroris raised identifying the conflicting name and its source. This prevents silent shadowing of built-in geometries and ambiguous duplicate registrations.If loading a particular entry point raises an exception other than a name collision (e.g. the plugin package is broken or missing), that entry point is silently skipped so that the rest of the registry is unaffected.
- Raises:
ValueError – If an entry-point name duplicates an already-registered geometry name.
- ad_hoc_diffractometer.factories.get_geometry(name: str)[source]#
Import:
ad_hoc_diffractometer.factories.get_geometryReturn the registered geometry callable by name.
This is the primitive lookup — it returns the callable that constructs the geometry, not an instance. Use make_geometry() if you want an AdHocDiffractometer instance directly.
- Parameters:
name (str) – Name of the geometry, as registered by @register_geometry (e.g. ‘psic’, ‘fourcv’, ‘kappa4cv’).
- Returns:
A callable that returns a configured AdHocDiffractometer for the named geometry.
- Return type:
callable
- Raises:
ValueError – If no geometry with that name is registered, with a message listing the available names.
Examples
>>> from ad_hoc_diffractometer import get_geometry >>> make_psic = get_geometry("psic") >>> make_psic() AdHocDiffractometer(name='psic', ...) >>> get_geometry("kappa4cv")(alpha_deg=50) AdHocDiffractometer(name='kappa4cv', ...)
- ad_hoc_diffractometer.factories.list_geometries() dict[str, type][source]#
Import:
ad_hoc_diffractometer.factories.list_geometriesReturn a copy of the geometry registry as {name: callable}.
Includes all built-in geometries (registered via
@register_geometryat import time) plus any third-party geometry plugins installed as entry points in the"ad_hoc_diffractometer.geometries"group.Entry-point discovery runs automatically the first time this function is called; subsequent calls use the already-populated registry.
- Returns:
Keys are geometry names (e.g.
'psic','fourcv'). Values are callables that return a configuredAdHocDiffractometer.- Return type:
Examples
>>> from ad_hoc_diffractometer import list_geometries >>> sorted(list_geometries()) ['fivec', 'fourch', 'fourcv', 'kappa4ch', 'kappa4cv', 'kappa6c', 'psic', 's2d2', 'sixc', 'zaxis'] >>> list_geometries()['psic']() # instantiate by name AdHocDiffractometer(name='psic', ...)
- ad_hoc_diffractometer.factories.make_geometry(name: str, **kwargs)[source]#
Import:
ad_hoc_diffractometer.factories.make_geometryInstantiate a geometry by name, passing keyword arguments to its constructor callable.
Looks up the geometry by name via get_geometry() and calls it with the supplied kwargs. This is the most convenient entry point for config-driven or programmatic geometry selection.
- Parameters:
name (str) – Name of the geometry (e.g. ‘psic’, ‘fourcv’, ‘kappa4cv’).
**kwargs – Keyword arguments forwarded to the geometry constructor. Most demo geometries take no arguments; kappa demo geometries accept alpha_deg.
- Returns:
A fully configured diffractometer geometry instance.
- Return type:
- Raises:
ValueError – If no geometry with that name is registered.
Examples
>>> from ad_hoc_diffractometer import make_geometry >>> make_geometry("psic") AdHocDiffractometer(name='psic', ...) >>> make_geometry("kappa4cv", alpha_deg=50) AdHocDiffractometer(name='kappa4cv', ...) >>> make_geometry("kappa6c", alpha_deg=55) AdHocDiffractometer(name='kappa6c', ...)
- ad_hoc_diffractometer.factories.register_geometry(func)[source]#
Import:
ad_hoc_diffractometer.factories.register_geometryDecorator that registers a geometry callable in _GEOMETRY_REGISTRY.
The function is stored under its own
__name__, so the registry key is always identical to the callable’s name. The function is returned unchanged; this decorator has no runtime effect on the callable itself.Third-party packages do not need to use this decorator — they can instead declare an entry point in the
"ad_hoc_diffractometer.geometries"group in theirpyproject.tomland the geometry will be discovered automatically.Example
@register_geometry def psic() -> AdHocDiffractometer: ...