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) and the default kappa tilt angle (KAPPA_ALPHA_DEFAULT)
used by the pre-built geometry functions in
ad_hoc_diffractometer.presets.
Pre-built geometries#
The 10 pre-built geometry factory functions (psic, fourcv,
fourch, sixc, kappa4cv, kappa4ch, kappa6c, zaxis,
s2d2, fivec) live in ad_hoc_diffractometer.presets.
Access them as:
import ad_hoc_diffractometer as ahd
g = ahd.presets.fourcv()
Extension via entry points#
Third-party packages can contribute additional geometry factories 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_factory_function"
The factory 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 factory accepts an optional basis keyword argument (defaulting to
the canonical convention for that geometry). Inside the factory, 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. |
|
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 factories from installed entry points. |
|
Return the registered factory function for the named geometry. |
|
Return a copy of the geometry registry as {name: factory_callable}. |
|
Instantiate a geometry by name, passing keyword arguments to its factory. |
|
Decorator that registers a geometry factory 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.presets).
- 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.presets).
- 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 factories from installed entry points.
Scans the
"ad_hoc_diffractometer.geometries"entry-point group for all installed packages (including this package itself) and adds any factories 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 factories 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 factory function for the named geometry.
This is the primitive lookup — it returns the callable factory, 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:
The factory function 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 >>> factory = get_geometry("psic") >>> factory() 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: factory_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 factory names (e.g.
'psic','fourcv'). Values are the callable factory functions.- 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 factory.
Looks up the factory for the named geometry 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 factory function. Most factories take no arguments; kappa factories 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 factory 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 factory 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 factory will be discovered automatically.Example
@register_geometry def psic() -> AdHocDiffractometer: ...