Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/api/tests/unit/deployments/test_beamline_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,12 @@ def test_renders_source_stage_walk_and_no_em_dash() -> None:
assert "`new`" in markdown
# the P6-50 nested constituents (a source-stage device) render as their own sub-table
assert "**SafetyStack constituents**" in markdown
# a source family present in the Catalog links up; a pending one renders plain (no fake link)
# a source family present in the Catalog links up; a pending one renders plain (no fake link).
# Beam is the loose bending-magnet source representation, intentionally never a catalog Family
# (see the InsertionDevice family note), so it is a stable "renders plain" example.
assert "](../../catalog/families.md)" in markdown
assert "`Mask`" in markdown
assert "[`Mask`](../../catalog/families.md)" not in markdown
assert "`Beam`" in markdown
assert "[`Beam`](../../catalog/families.md)" not in markdown
# the folded source-area modelling note and the confirm marker render
assert "no Conditioner Role" in markdown
assert "`confirm`" in markdown
Expand Down
20 changes: 20 additions & 0 deletions apps/api/tests/unit/equipment/test_register_fixture_decider.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,26 @@ def test_decide_rejects_exactly_one_slot_with_zero_bindings() -> None:
assert "Exactly1" in exc_info.value.reason


@pytest.mark.unit
def test_decide_allows_zero_or_one_slot_left_unbound() -> None:
assembly_id = uuid4()
slot = _slot("camera", cardinality=SlotCardinality.ZERO_OR_ONE)
context = RegisterFixtureContext(
assembly_state=_assembly(assembly_id, slots=frozenset({slot})),
)
events = register_fixture.decide(
state=None,
command=RegisterFixture(assembly_id=assembly_id),
context=context,
now=_NOW,
new_id=uuid4(),
registered_by=_TEST_ACTOR_ID,
)
assert len(events) == 1
assert isinstance(events[0], FixtureRegistered)
assert events[0].slot_asset_bindings == frozenset()


@pytest.mark.unit
def test_decide_rejects_unknown_slot_in_bindings() -> None:
assembly_id = uuid4()
Expand Down
21 changes: 15 additions & 6 deletions catalog/catalog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ families:
note: "Beam-defining four-blade slit Family (four independently-driven blades forming a variable horizontal/vertical opening). 2-BM: ConditioningSlit (A-station L3-20) and SampleSlit (B-station)."
- name: Aperture
note: "Patterned beam-shaping aperture Family: a fixed-pattern mask positioned in the beam as a unit. Distinct from Slit (a variable opening defined by four driven blades) and from a passive Mask (a fixed beam-defining opening that does not move); the discriminator is the element's nature (a fixed code pattern), not that it is driven. 2-BM: the coded aperture for compressive-sensing tomography, fine-positioned by ApertureFineDrive."
- name: Mask
note: "Passive beam-defining mask Family: a plain fixed-opening element that shapes or limits the beam, positioned as a unit. Distinct from Slit (a variable opening via four driven blades) and Aperture (a fixed code pattern); the discriminator is the element's nature (a plain fixed opening). One Family spans the permanently-fixed masks and the movable safety-mask variant (movability is a per-Asset setting, the Table / InsertionDevice precedent). 2-BM: the M3-24 front-end exit mask. TomoWISE: the FM1 / FM2 fixed masks and the MSM movable safety mask. Earned in once two deployments shared it; beam-effect modelling stays deferred (the passive beam-path tier)."
- name: Mirror
note: "Beam-steering grazing-incidence mirror optic Family. 2-BM: the Y3-30 mirror (distinct from its MirrorTable support, which is Family Table)."
- name: Monochromator
Expand Down Expand Up @@ -273,21 +275,28 @@ models:
assemblies:
- name: Optics
note: >-
Reusable optics core: turret, objectives, virtual objective selector, and
propagation-distance stage. Presents nothing on its own; composed by detector Assemblies.
Reusable optics core: turret, objectives, virtual objective selector, and an
optional propagation-distance stage. Presents nothing on its own; composed by
detector Assemblies. propagation_distance is ZeroOrOne because some deployments
drive propagation with a shared facility stage rather than a per-Optics rail
(2-BM binds a per-microscope PropagationDistance; TomoWISE shares one detector
gantry rail across both microscopes, so its microscopes leave the slot empty).
presents_as: []
required_slots:
- { slot_name: turret, required_families: [LinearStage], cardinality: Exactly1 }
- { slot_name: objectives, required_families: [Objective], cardinality: OneOrMore }
- { slot_name: objective_selector, required_families: [PseudoAxis], cardinality: Exactly1 }
- { slot_name: propagation_distance, required_families: [LinearStage], cardinality: Exactly1 }
- { slot_name: propagation_distance, required_families: [LinearStage], cardinality: ZeroOrOne }
- name: Microscope
note: >-
Full scintillator-relay detector: the Optics sub-assembly plus camera and
scintillator leaf slots. Presents the Detector Role. Zero required_wires in v1.
Full scintillator-relay detector: the Optics sub-assembly plus an optional camera
and a scintillator leaf slot. Presents the Detector Role. Zero required_wires in v1.
camera is ZeroOrOne because a microscope can be camera-agnostic, drawing from a
shared camera pool (2-BM binds its camera per microscope; TomoWISE's two microscopes
share four interchangeable cameras on one gantry, so each leaves the slot empty).
presents_as: [Detector]
required_slots:
- { slot_name: camera, required_families: [Camera], cardinality: Exactly1 }
- { slot_name: camera, required_families: [Camera], cardinality: ZeroOrOne }
- { slot_name: scintillator, required_families: [Scintillator], cardinality: Exactly1 }
required_sub_assemblies:
- { slot_name: optics, sub_assembly: Optics }
163 changes: 144 additions & 19 deletions deployments/tomowise/beamline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ front-end:
enclosure: TomoWISE-optics
intro: "Fixed and movable masks plus the heat absorber that shape the beam and absorb power before the optics hutch."
note: >
z from the CPMU14 source (TDR Table 7.1). Mask/heat-absorber Families are
not yet in the catalog (they render as plain text pending the passive
beam-path tier); bound loosely here by design intent.
z from the CPMU14 source (TDR Table 7.1). Mask is now a shared catalog
Family; the heat-absorber Family is not yet in the catalog (it renders as
plain text pending the passive beam-path tier), bound loosely by design intent.
devices:
- name: FM1
family: Mask
Expand Down Expand Up @@ -109,7 +109,7 @@ front-end:
new: true
confirm: true
power_budget: 4 kW
note: "heat absorber; movable masks MM1/MM2 alongside"
note: "front-end heat absorber protecting the safety shutter; a second, near-identical heat absorber sits in the optics-hutch safety unit (SU) with SS1/SS2 (TDR Table 8.1), not separately modelled here"

optics-hutch:
stage: source
Expand All @@ -133,8 +133,7 @@ optics-hutch:
z_mm: 24280
new: true
confirm: true
opening: 60 urad
note: "white-beam slits (DM1)"
note: "water-cooled white-beam slits (WBS) in DM1; DM1 also carries a separate beam-defining mask (BM, water-cooled Cu, 60 urad opening matching the MLM acceptance) and a bremsstrahlung collimator, not separately modelled here"
- name: PFU
family: Filter
z_mm: 25200
Expand All @@ -148,7 +147,7 @@ optics-hutch:
z_mm: 25900
new: true
confirm: true
coating: W/Si or W/B4C bilayers, d=2.5 nm, 140 bilayers (OPT-1)
coating: W/SiC or W/B4C bilayers (TDR text); Table 8.3 lists W/Si; d=2.5 nm, 140 bilayers (OPT-1)
energy_range: 20 to 65 keV
bandwidth: dE/E ~ 1.8%
bragg_range: 3.84 to 12.6 mrad
Expand All @@ -165,7 +164,7 @@ optics-hutch:
new: true
confirm: true
transmission: 1e-4 to 1
note: "metal filter unit (third filter stage); on the experiment-hutch end wall"
note: "metal filter unit (third filter stage, Fe/Cu carriages, NanoMAX design); at the beginning of the experiment hutch"

safety-shutters:
stage: source
Expand Down Expand Up @@ -247,8 +246,15 @@ microtomography-endstation:
nanotomography-endstation:
stage: sample
enclosure: TomoWISE-experiment
intro: "The nanotomography endstation, ~49-51 m from source: the KB mirror pair focuses the undulator beam for 200-nm-class cone-beam imaging. The sample stage is not yet specified."
note: "Mirror Family reused for the KB pair. The nano sample stage is deferred to procurement (NANO-1)."
intro: "The nanotomography endstation, ~49-51 m from source: the KB mirror pair focuses the undulator beam for 200-nm-class cone-beam imaging. The sample manipulator is a six-axis stack on a granite support, conceptually similar to the microtomography endstation but about a factor of ten more precise."
note: >
Mirror Family reused for the KB pair. The TDR specifies the sample
manipulator in Table 9.5 (it is NOT deferred to procurement): the stack
reuses the same Families as the microtomography endstation (Table,
TiltStage, LinearStage, RotaryStage), with a "(target)" model per axis
carried as model_target pending procurement (NANO-1). The most critical
axis is the rotary: Abbe error from wobble and eccentricity must not
exceed 100 nm at 100 mm sample height.
devices:
- name: KB
family: Mirror
Expand All @@ -258,19 +264,75 @@ nanotomography-endstation:
type: fixed-curvature graded-multilayer KB pair
focal_spot: 205 x 196 nm @ 30 keV; 196 x 80 nm @ 45 keV
note: "Kirkpatrick-Baez focusing mirrors for nanotomography"
- name: NanoSampleStage
family: NanoPositioner
- name: NanoGranite
family: Table
z_mm: 49000
new: true
confirm: true
note: "single granite support housing the KB optics, the sample manipulator, and the detector stage"
- name: NanoTilt
family: TiltStage
new: true
confirm: true
travel: 2 deg
resolution: 5 mdeg
model_target: Huber 5202.80 (NANO-1)
note: "Tilt X; aligns the rotation axis to the beam vertical angle"
- name: NanoCoarseX
family: LinearStage
new: true
confirm: true
travel: 50 mm
model_target: Huber 5101.20 (NANO-1)
note: "Xt; centre-of-rotation alignment and flat-field acquisition"
- name: NanoCoarseY
family: LinearStage
new: true
confirm: true
travel: 50 to 100 mm
model_target: Huber 5103.A20-90 (NANO-1)
note: "Yt; sample height adjustment"
- name: NanoCoarseZ
family: LinearStage
new: true
confirm: "specification deferred to procurement (NANO-1); must meet Abbe error compatible with 200 nm resolution"
note: "nanotomography sample stage; not yet specified in the TDR"
confirm: true
travel: 250 to 300 mm
resolution: <0.5 um
model_target: Zaber X-LDQ-AE (NANO-1)
note: "Zt; brings the rotation axis into the KB focus, then translates toward the detector"
- name: NanoRotary
family: RotaryStage
new: true
confirm: true
max_speed: ">60 rpm"
resolution: 1 mdeg
eccentricity: <100 nm at sample height
encoder: TTL, 3600 pulses per rotation
model_target: Lab Motion Systems RT100AS (NANO-1)
note: "Rot y; continuous nanotomographic rotation, radial error <100 nm / axial error <50 nm"
- name: NanoSamplePositioning
family: LinearStage
new: true
confirm: true
travel: +/-6 mm (Xs, Zs)
resolution: 0.1 um
model_target: Lab Motion Systems XY150B-12 (NANO-1)
note: "Xs/Zs sample-to-CoR centring; same model as the micro endstation positioning"

# --- DETECTION STAGE: the shared detector gantry ---

detector:
stage: detection
enclosure: TomoWISE-experiment
intro: "One detector gantry on 7 m floor rails serves both endstations (45 m to the 52 m hutch wall), carrying interchangeable microscopes and cameras. A removable flight tube reduces air scatter."
note: "Camera and microscope-optics models are design targets, chosen in project year 2 (DET-1, DET-2)."
note: >
The two microscopes are composed as Microscope Assemblies (Housing-anchored,
reusing the cross-facility catalog assembly): each has a turret, objectives,
and a selector over a scintillator. The four cameras and the gantry propagation
rail are shared across both microscopes, so the assembly's camera and
propagation_distance slots are left empty (ZeroOrOne). Camera models are design
targets (DET-1); the Optique Peter optics model is bound from the 2-BM catalog
candidate pending confirmation (DET-2).
devices:
- name: DetectorGantry
family: Table
Expand All @@ -280,19 +342,82 @@ detector:
range: 45 m to 52 m
note: "shared detector gantry"
- name: MicLFOV
family: Microscope
family: Housing
model: optique_peter_micrx080
new: true
confirm: true
magnification: 1 to 2x
numerical_aperture: ">0.2"
note: "large-field-of-view microscope (DET-2)"
cora: "Assembly(Microscope) presenting the Detector Role; Optics sub-assembly = turret + objectives + selector. The camera and propagation_distance slots are left empty (shared cameras + DetectorGantry rail)."
note: "large-field-of-view microscope; Optique Peter housing, model is the 2-BM candidate pending TomoWISE confirmation (DET-2)"
constituents:
- name: Turret
family: LinearStage
new: true
confirm: true
note: "objective changer: switches objectives without intervening in the setup (TDR p57)"
- name: Objective_1x
family: Objective
new: true
confirm: true
magnification: 1.0
- name: Objective_2x
family: Objective
new: true
confirm: true
magnification: 2.0
- name: ObjectiveSelector
family: PseudoAxis
new: true
confirm: true
note: "virtual objective picker over the turret"
- name: Scintillator
family: Scintillator
new: true
confirm: true
material: LSO or LuAG:Ce
thickness: 7 to 15 um
- name: MicHR
family: Microscope
family: Housing
model: optique_peter_micrx080
new: true
confirm: true
magnification: 4x / 10x / 20x
numerical_aperture: ">0.4"
note: "high-resolution microscope (DET-2)"
cora: "Assembly(Microscope) presenting the Detector Role; Optics sub-assembly = turret + objectives + selector. The camera and propagation_distance slots are left empty (shared cameras + DetectorGantry rail)."
note: "high-resolution microscope; Optique Peter housing, model is the 2-BM candidate pending TomoWISE confirmation (DET-2)"
constituents:
- name: Turret
family: LinearStage
new: true
confirm: true
note: "objective changer: switches objectives without intervening in the setup (TDR p57)"
- name: Objective_4x
family: Objective
new: true
confirm: true
magnification: 4.0
- name: Objective_10x
family: Objective
new: true
confirm: true
magnification: 10.0
- name: Objective_20x
family: Objective
new: true
confirm: true
magnification: 20.0
- name: ObjectiveSelector
family: PseudoAxis
new: true
confirm: true
note: "virtual objective picker over the turret"
- name: Scintillator
family: Scintillator
new: true
confirm: true
material: LuAG:Ce
thickness: 7 to 15 um
- name: CameraI
family: Camera
new: true
Expand Down
10 changes: 6 additions & 4 deletions docs/deployments/tomowise/equipment/detector.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ TomoWISE has a single detector system on a gantry that travels the experiment hu

## Microscopes

Interchangeable visible-light microscopes couple the scintillator image to the cameras. The optics vendor is a design decision deferred to project year 2 (DET-2); the Optique Peter MICRX080 is the reference.
Interchangeable visible-light microscopes (scintillator, objective, 45 deg mirror, CMOS camera) couple the scintillator image to the cameras, built for sensors up to 60 mm diagonal. Each is **composed as the cross-facility `Microscope` Assembly** that 2-BM also uses, rather than a loose family: a `Housing` anchors an `Optics` sub-assembly (a turret, the objectives, and a virtual objective selector for switching magnification "without intervening in the setup") over a `Scintillator`. The Optique Peter optics model from 2-BM, `optique_peter_micrx080`, is **bound** on each Housing as the design-target candidate (the TDR names only the vendor; confirmation is DET-2).

- **MicLFOV** (Family `Microscope`, not yet in the catalog): large field of view, 1-2x magnification, NA > 0.2.
- **MicHR** (Family `Microscope`, not yet in the catalog): high resolution, 4x / 10x / 20x, NA > 0.4.
Because TomoWISE's two microscopes share the four cameras and the one `DetectorGantry` propagation rail, the assembly's `camera` and `propagation_distance` slots are **decoupled**: the catalog assembly was generalized to make both `ZeroOrOne`, and each microscope leaves them empty. The cameras are modelled as separate shared Assets (below); the gantry provides the propagation distance.

- **MicLFOV** (Assembly `Microscope`, Housing model `optique_peter_micrx080`): large field of view, 1-2x magnification, NA > 0.2; objectives 1x / 2x.
- **MicHR** (Assembly `Microscope`, Housing model `optique_peter_micrx080`): high resolution, 4x / 10x / 20x, NA > 0.4.

## Cameras

Expand All @@ -24,4 +26,4 @@ Four cameras span the throughput-versus-speed-versus-resolution trade. The model
- **Camera III** (Family `Camera`): ~4 Mpix, > 2,000 fps. Streaming.
- **Camera IV** (Family `Camera`): 150 Mpix, 54 x 40 mm sensor, 3.76 um pixel. Matches the large-sensor device already procured for DanMAX.

The camera models, the microscope vendor, and the trigger path are the main detector-side [open questions](../questions.md). See [Inventory](../inventory.md) for the Asset tree.
The camera models, the bound microscope-optics model confirmation (DET-2), and the trigger path are the main detector-side [open questions](../questions.md). See [Inventory](../inventory.md) for the Asset tree.
Loading
Loading