From d17f3deefa3dd19d2f14e1cd097c52824f4a07be Mon Sep 17 00:00:00 2001 From: x10da Date: Tue, 5 May 2026 14:53:49 +0200 Subject: [PATCH] Introduced nexus data structure (same as Debye) --- pyproject.toml | 4 + .../bec_widgets/widgets/x10da_parameters.py | 239 +++++++++++ superxas_bec/device_configs/x10da_optics.yaml | 396 ++++++++++++++++++ .../device_configs/x10da_standard_config.yaml | 6 +- .../file_writer/superxas_nexus_structure.py | 375 ++++++++++++----- 5 files changed, 921 insertions(+), 99 deletions(-) create mode 100644 superxas_bec/bec_widgets/widgets/x10da_parameters.py create mode 100644 superxas_bec/device_configs/x10da_optics.yaml diff --git a/pyproject.toml b/pyproject.toml index 67c4e70..4228ac0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,11 @@ classifiers = [ "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering", ] + dependencies = [ + "numpy", + "scipy", + "xrt", "websockets", ] diff --git a/superxas_bec/bec_widgets/widgets/x10da_parameters.py b/superxas_bec/bec_widgets/widgets/x10da_parameters.py new file mode 100644 index 0000000..73afd99 --- /dev/null +++ b/superxas_bec/bec_widgets/widgets/x10da_parameters.py @@ -0,0 +1,239 @@ +""" +X10DA / SuperXAS Beamline Parameters. +This file describes the parameter of each component of the SuperXAS beamline +to be used for raytracing and geometrical calculations. +""" + +import os +import numpy as np +from collections import namedtuple + +import xrt.backends.raycing.materials as rm + +# if os.environ.get("USE_XRT", "True").lower() in ("1", "true", "yes"): +# import xrt.backends.raycing.materials as rm # type: ignore +# else: +# class _DummyClass: +# def __init__(self, *args, **kwargs): +# pass +# class _DummyMaterials: +# Material = _DummyClass +# CrystalSi = _DummyClass +# rm = _DummyMaterials() + +# XRT definitions +filterBeryl = rm.Material('Be', rho=1.85, kind='plate') +filterDiamond = rm.Material('C', rho=3.52, kind='plate') +filterGraphite = rm.Material('C', rho=2.266, kind='plate') + +stripeSi = rm.Material('Si', rho=2.33) +stripePt = rm.Material('Pt', rho=21.45) +stripeRh = rm.Material('Rh', rho=12.41) +stripeCr = rm.Material('Cr', rho=7.14) +stripePyrex = rm.Material('Si', rho=2.20) # Use Si as bare element and the density of SiO2 + +si111_1 = rm.CrystalSi(hkl=(1, 1, 1), tK=77) # first xtal surface +si311_1 = rm.CrystalSi(hkl=(3, 1, 1), tK=77) # first xtal surface +si333_1 = rm.CrystalSi(hkl=(3, 3, 3), tK=77) # first xtal surface +si511_1 = rm.CrystalSi(hkl=(5, 1, 1), tK=77) # first xtal surface +si111_2 = rm.CrystalSi(hkl=(1, 1, 1), tK=77) # second xtal surface +si311_2 = rm.CrystalSi(hkl=(3, 1, 1), tK=77) # second xtal surface +si333_2 = rm.CrystalSi(hkl=(3, 3, 3), tK=77) # second xtal surface +si511_2 = rm.CrystalSi(hkl=(5, 1, 1), tK=77) # second xtal surface + +filterDiamond = rm.Material('C', rho=3.52, kind='plate') +filterBe = rm.Material('Be', rho=1.85, kind='plate') +filterSi3N4 = rm.Material(['Si', 'N'], quantities=[3, 4], rho=3.44, kind='plate') +filterAl = rm.Material('Al', rho=2.69, kind='plate') +filterGraphite = rm.Material('C', rho=2.266, kind='plate') + +sourceHeight = 0 + +#Synchrotron +synchrotron = namedtuple('synchrotron', ['eE', 'eI', 'eEspread', + 'eEpsilonX', 'eEpsilonZ', 'betaX', 'betaZ']) + +sls1 = synchrotron( + eE = 2.4, + eI = 0.4, + eEspread=0.878e-3, + eEpsilonX=5.63, + eEpsilonZ=0.007, + betaX=0.45, + betaZ=14.4, + ) + +sls2 = synchrotron( + eE=2.7, + eI=0.4, + eEspread=1.147e-3, + eEpsilonX=0.156, + eEpsilonZ=0.01, + betaX=0.18, + betaZ=4.6, + ) + +# Source +bendingMagnet = namedtuple('bendingMagnet', ['name', 'center', 'sync', 'B0']) + +sls1_29t = bendingMagnet( + name='FE-BM-SLS1-2.9T', + center=(0, 0, 0), + sync=sls1, + B0=2.9,) + +sls2_21t = bendingMagnet( + name='FE-BM-SLS2-2.1T', + center=(0, 0, 0), + sync=sls1, + B0=2.1,) + +# FE slits +slits = namedtuple('slits', ['name', 'center', 'maxDivH', 'maxDivV']) + +feSlits = slits( + name='FE-SLITS', + center=(0, 5290, sourceHeight), + maxDivH=1.8e-3, + maxDivV=0.8e-3,) + +# Filters +filt = namedtuple('filt', ['name', 'center', 'pitch', 'limPhysX', 'limPhysY', 'surface', 'material', 'thickness']) + +feWindow = filt( + name='FE-WINDOW', + center=(0., 6158, sourceHeight), + pitch=np.pi/2, + limPhysX=(-6, 6), + limPhysY=(-3., 3.), + surface='None', + material=filterDiamond, + thickness=0.1,) +feWindow = feWindow._replace(surface='CVD Diamond window {0:0.0f} $\mu$m'.format(feWindow.thickness*1e3)) + +feFilt = filt( + name='FE-FI', + center=(0., 6590, sourceHeight), + pitch=np.pi/2, + limPhysX=(-15, 15), + limPhysY=(-10, 10), + surface='None', + material=filterGraphite, + thickness=0.25,) +feFilt = feFilt._replace(surface='Graphite filter {0:0.0f} $\mu$m'.format(feFilt.thickness*1e3)) + +# Collimating mirror +collimatingMirror = namedtuple('collimatingMirror', ['name', + 'center', 'surface', 'material', 'limPhysX', 'limPhysY', + 'limOptX', 'limOptY', 'R', 'pitch', 'jack1', 'jack2', 'jack3', + 'tx1', 'tx2']) + +cm = collimatingMirror( + name='FE-CM', + center=[0, 7618, sourceHeight], + surface=('Rh','Si','Pt'), + material=(stripeRh, stripeSi, stripePt), + limPhysX=(-30, 30), + limPhysY=(-600, 600), + limOptX=((11, -2, -21), (21, 8, -5)), + limOptY=((-500, -500, -500), (500, 500, 500)), + R=[3e6, 15e6], + pitch=[1.4e-3, 4.5e-3], + jack1=[0., 7210., 0.], #Tripod X, Y, Z (global) + jack2=[-210., 8310., 0.], + jack3=[210., 8310., 0.], + tx1=[0.0, -575.5], # X-Stage 1 [x, y] (local) + tx2=[0.0, 575],) # X-Stage 2 + +apertures = namedtuple('apertures', ['name', 'center', 'opening']) + +fePS = apertures( + name='FE-PS', + center=[0, 8760, sourceHeight], + opening=[-39/2, 39/2, -10, 29]) # left, right, bottom, top + +opWbBsBlock = apertures( + name='OP-WB-BS-BLOCK', + center=[0., 13606-135, sourceHeight], + opening=[-18., 18., 42, 76]) # left, right, bottom, top + +opSlits = apertures( + name='OP-SLITS', + center=[0, 14145-135, sourceHeight], + opening=[-35/2, 35/2, 47.5, 82.5]) + +# Monochromator +monochromator = namedtuple('monochromator', ['name', 'center', + 'xtal', 'material1', 'material2', 'xtalWidth', 'xtalOffsetX', + 'xtalLength1', 'xtalLength2', 'xtalGap', 'rotOffset', + 'heightOffset', 'braggLim', 'jack1', 'jack2', 'jack3', 'tx']) + +mo1 = monochromator( + name='OP-CCM1', + center=[0., 11670-135, sourceHeight], + xtal=('Si311','Si111'), + material1=(si311_1, si111_1), + material2=(si311_2, si111_2), + xtalWidth = (20, 20), + xtalOffsetX=(-19.2, 19.2), + xtalLength1 = (60, 60), + xtalLength2 = (60, 60), + xtalGap = (8, 8), + rotOffset = 6, # not sure what it is + heightOffset = 8.5, # not sure what it is + braggLim = [4, 35], + jack1=[0., 11350., 0.], #Tripod not available! + jack2=[-400., 12350., 0.], + jack3=[400., 12350., 0.], + tx=0.0,) # X-Stage [x] + +# Focusing mirror +focusingMirror = namedtuple('focusingMirror', ['name', 'center', + 'surfaceToroid', 'materialToroid', + 'limPhysXToroid', 'limPhysYToroid', + 'limOptXToroid', 'limOptYToroid', + 'R', 'pitch', 'r', 'xToroid', 'hToroid', 'jack1', 'jack2', 'jack3', + 'tx1', 'tx2']) + +fm = focusingMirror( + name='OP-FM', + center=[0., 15580-135, sourceHeight], + surfaceToroid=('Rh', 'Pt'), + materialToroid=(stripeRh, stripePt), + limPhysXToroid=(-54., 54.), + limPhysYToroid=(-565., 565.), + limOptXToroid=((90.25, 41.75), (51.75, 5.75)), # With old VME axis, no absolute value! + limOptYToroid=((-500., -500.), (500., 500.)), + R=[3e6, 15e6], + pitch=[1.4e-3, 4.5e-3], + r=[30, 20], + xToroid=[24.126, -22,874], # offset in local x + hToroid=[7., 11.3], # depth of the cylinder at x = xCylinder1 and x = xCylinder2. + jack1=[0., 14980., 0.], + jack2=[-75., 16180., 0.], + jack3=[75., 16180., 0.], + tx1=[0., -575.], # X-Stage 1 [x, y] + tx2=[0., 575.],) # X-Stage 2 [x, y] + +ehWindow = filt( + name='EH-WINDOW', + center=(0., 22225-135, sourceHeight), + pitch=np.pi/2, + limPhysX=(-10., 10.), + limPhysY=(17.5, 92.5), + surface='None', + material=filterBe, + thickness=0.25,) +ehWindow = ehWindow._replace(surface='Beryllium window {0:0.0f} $\mu$m'.format(ehWindow.thickness*1e3)) + +# Sample +sample = namedtuple('sample', ['name', 'center']) + +smpl = sample( + name='OP-SMPL', + center=[0, 24000-135, sourceHeight],) + + + + + diff --git a/superxas_bec/device_configs/x10da_optics.yaml b/superxas_bec/device_configs/x10da_optics.yaml new file mode 100644 index 0000000..9947ba4 --- /dev/null +++ b/superxas_bec/device_configs/x10da_optics.yaml @@ -0,0 +1,396 @@ + +################################### +## Monochromator ## +################################### + +mo1_try: + readoutPriority: baseline + description: Monochromator Y Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +mo1_trx: + readoutPriority: baseline + description: Monochromator X Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Optics Slits + Beam Monitor 1 ## +################################### + +# sl1_trxr: +# readoutPriority: baseline +# description: Optics slits 1 X-translation Ring-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:TRXR +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_trxw: +# readoutPriority: baseline +# description: Optics slits 1 X-translation Wall-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:TRXW +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_tryb: +# readoutPriority: baseline +# description: Optics slits 1 Y-translation Bottom-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:TRYB +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_tryt: +# readoutPriority: baseline +# description: Optics slits 1 X-translation Top-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:TRYT +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# bm1_try: +# readoutPriority: baseline +# description: Beam Monitor 1 Y-translation +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-BM1:TRY +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_centerx: +# readoutPriority: baseline +# description: Optics slits 1 X-center +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:CENTERX +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_gapx: +# readoutPriority: baseline +# description: Optics slits 1 X-gap +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:GAPX +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_centery: +# readoutPriority: baseline +# description: Optics slits 1 Y-center +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:CENTERY +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl1_gapy: +# readoutPriority: baseline +# description: Optics slits 1 Y-gap +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL1:GAPY +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +################################### +## Focusing Mirror ## +################################### + +# fm_trxu: +# readoutPriority: baseline +# description: Focusing Mirror X-translation upstream +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-FM:TRXU +# onFailure: retry +# enabled: true +# softwareTrigger: false + +# fm_trxd: +# readoutPriority: baseline +# description: Focusing Mirror X-translation downstream +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-FM:TRXD +# onFailure: retry +# enabled: true +# softwareTrigger: false + +# fm_tryd: +# readoutPriority: baseline +# description: Focusing Mirror Y-translation downstream +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-FM:TRYD +# onFailure: retry +# enabled: true +# softwareTrigger: false + +# fm_tryur: +# readoutPriority: baseline +# description: Focusing Mirror Y-translation upstream ring +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-FM:TRYUR +# onFailure: retry +# enabled: true +# softwareTrigger: false + +# fm_tryuw: +# readoutPriority: baseline +# description: Focusing Mirror Y-translation upstream wall +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-FM:TRYUW +# onFailure: retry +# enabled: true +# softwareTrigger: false + +fm_bnd: + readoutPriority: baseline + description: Focusing Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRB + onFailure: retry + enabled: true + softwareTrigger: false + +# fm_bnd_radius: +# readoutPriority: baseline +# description: Focusing Mirror Bending Radius +# deviceClass: ophyd.EpicsSignalRO +# deviceConfig: +# read_pv: X10DA-CPCL-FM:BNDFORCE +# onFailure: retry +# readOnly: true +# enabled: true +# softwareTrigger: false + +fm_rotx: + readoutPriority: baseline + description: Focusing Morror Pitch + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP-MI1:pitch + onFailure: retry + enabled: true + softwareTrigger: false + +fm_roty: + readoutPriority: baseline + description: Focusing Morror Yaw + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP-MI1:yaw + onFailure: retry + enabled: true + softwareTrigger: false + +fm_rotz: + readoutPriority: baseline + description: Focusing Morror Roll + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP-MI1:roll + onFailure: retry + enabled: true + softwareTrigger: false + +fm_trx: + readoutPriority: baseline + description: Focusing Morror Center Point X + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP-MI1:trans + onFailure: retry + enabled: true + softwareTrigger: false + +fm_try: + readoutPriority: baseline + description: Focusing Morror Center Point Y + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP-MI1:y + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Optics Slits + Beam Monitor 2 ## +################################### + +# sl2_trxr: +# readoutPriority: baseline +# description: Optics slits 2 X-translation Ring-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:TRXR +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_trxw: +# readoutPriority: baseline +# description: Optics slits 2 X-translation Wall-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:TRXW +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_tryb: +# readoutPriority: baseline +# description: Optics slits 2 Y-translation Bottom-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:TRYB +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_tryt: +# readoutPriority: baseline +# description: Optics slits 2 X-translation Top-edge +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:TRYT +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# bm2_try: +# readoutPriority: baseline +# description: Beam Monitor 2 Y-translation +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-BM2:TRY +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_centerx: +# readoutPriority: baseline +# description: Optics slits 2 X-center +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:CENTERX +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_gapx: +# readoutPriority: baseline +# description: Optics slits 2 X-gap +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:GAPX +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_centery: +# readoutPriority: baseline +# description: Optics slits 2 Y-center +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:CENTERY +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits + +# sl2_gapy: +# readoutPriority: baseline +# description: Optics slits 2 Y-gap +# deviceClass: ophyd.EpicsMotor +# deviceConfig: +# prefix: X10DA-OP-SL2:GAPY +# onFailure: retry +# enabled: true +# softwareTrigger: false +# deviceTags: +# - optics +# - slits \ No newline at end of file diff --git a/superxas_bec/device_configs/x10da_standard_config.yaml b/superxas_bec/device_configs/x10da_standard_config.yaml index 1996cfb..75e71d6 100644 --- a/superxas_bec/device_configs/x10da_standard_config.yaml +++ b/superxas_bec/device_configs/x10da_standard_config.yaml @@ -52,9 +52,9 @@ mo1_bragg: # enabled: true # softwareTrigger: false -## Remaining optics hutch -# optics_config: -# - !include ./x10da_optics.yaml +# Remaining optics hutch +optics_config: + - !include ./x10da_optics.yaml ################################### ## Experimental Hutch ## diff --git a/superxas_bec/file_writer/superxas_nexus_structure.py b/superxas_bec/file_writer/superxas_nexus_structure.py index 867ce48..a77e89e 100644 --- a/superxas_bec/file_writer/superxas_nexus_structure.py +++ b/superxas_bec/file_writer/superxas_nexus_structure.py @@ -1,5 +1,6 @@ from bec_server.file_writer.default_writer import DefaultFormat +import superxas_bec.bec_widgets.widgets.x10da_parameters as bl class SuperXASNexusStructure(DefaultFormat): """Nexus Structure for SuperXAS""" @@ -12,102 +13,6 @@ class SuperXASNexusStructure(DefaultFormat): instrument = entry.create_group(name="instrument") instrument.attrs["NX_class"] = "NXinstrument" - ################### - ## mo1_bragg specific information - ################### - - # Logic if device exist - if "mo1_bragg" in self.device_manager.devices: - - monochromator = instrument.create_group(name="monochromator") - monochromator.attrs["NX_class"] = "NXmonochromator" - crystal = monochromator.create_group(name="crystal") - crystal.attrs["NX_class"] = "NXcrystal" - - # Create a dataset - chemical_formular = crystal.create_dataset(name="chemical_formular", data="Si") - chemical_formular.attrs["NX_class"] = "NX_CHAR" - - # Create a softlink - d_spacing = crystal.create_soft_link( - name="d_spacing", - target="/entry/collection/devices/mo1_bragg/mo1_bragg_crystal_current_d_spacing/value", - ) - d_spacing.attrs["NX_class"] = "NX_FLOAT" - - offset = crystal.create_soft_link( - name="offset", - target="/entry/collection/devices/mo1_bragg/mo1_bragg_crystal_current_offset/value", - ) - offset.attrs["NX_class"] = "NX_FLOAT" - - reflection = crystal.create_soft_link( - name="reflection", - target="/entry/collection/devices/mo1_bragg/mo1_bragg_crystal_current_xtal_string/value", - ) - reflection.attrs["NX_class"] = "NX_CHAR" - - ################## - ## cm mirror specific information - ################### - - collimating_mirror = instrument.create_group(name="collimating_mirror") - collimating_mirror.attrs["NX_class"] = "NXmirror" - - cm_substrate_material = collimating_mirror.create_dataset( - name="substrate_material", data="Si" - ) - cm_substrate_material.attrs["NX_class"] = "NX_CHAR" - - cm_bending_radius = collimating_mirror.create_soft_link( - name="sagittal radius", - target="/entry/collection/devices/cm_bnd_radius/cm_bnd_radius/value", - ) - cm_bending_radius.attrs["NX_class"] = "NX_FLOAT" - cm_bending_radius.attrs["units"] = "km" - - cm_incidence_angle = collimating_mirror.create_soft_link( - name="incidence angle", target="/entry/collection/devices/cm_rotx/cm_rotx/value" - ) - cm_incidence_angle.attrs["NX_class"] = "NX_FLOAT" - - cm_yaw_angle = collimating_mirror.create_soft_link( - name="incident angle", target="/entry/collection/devices/cm_roty/cm_roty/value" - ) - cm_yaw_angle.attrs["NX_class"] = "NX_FLOAT" - - ################## - ## fm mirror specific information - ################### - - focusing_mirror = instrument.create_group(name="focusing_mirror") - focusing_mirror.attrs["NX_class"] = "NXmirror" - - fm_substrate_material = focusing_mirror.create_dataset(name="substrate_material", data="Si") - fm_substrate_material.attrs["NX_class"] = "NX_CHAR" - - fm_bending_radius = focusing_mirror.create_soft_link( - name="sagittal radius", - target="/entry/collection/devices/fm_bnd_radius/fm_bnd_radius/value", - ) - fm_bending_radius.attrs["NX_class"] = "NX_FLOAT" - - fm_incidence_angle = focusing_mirror.create_soft_link( - name="incidence angle", - target="/entry/collection/devices/fm_incidence_angle/fm_incidence_angle/value", - ) - fm_incidence_angle.attrs["NX_class"] = "NX_FLOAT" - - fm_yaw_angle = focusing_mirror.create_soft_link( - name="yaw angle", target="/entry/collection/devices/fm_roty/fm_roty/value" - ) - fm_yaw_angle.attrs["NX_class"] = "NX_FLOAT" - - fm_roll_angle = focusing_mirror.create_soft_link( - name="roll angle", target="/entry/collection/devices/fm_rotz/fm_rotz/value" - ) - fm_roll_angle.attrs["NX_class"] = "NX_FLOAT" - ################## ## source specific information ################### @@ -123,3 +28,281 @@ class SuperXASNexusStructure(DefaultFormat): probe = source.create_dataset(name="probe", data="X-ray") probe.attrs["NX_class"] = "NX_CHAR" + + if "curr" in self.device_manager.devices: + ring_current = source.create_soft_link( + name="ring_current", + target="/entry/collection/devices/curr/curr/value", + ) + ring_current.attrs["NX_class"] = "NX_FLOAT" + ring_current.attrs["units"] = "mA" + + ################### + ## mo1_bragg specific information + ################### + + ## Logic if device exist + if "mo1_bragg" in self.device_manager.devices: + + monochromator = instrument.create_group(name="monochromator") + monochromator.attrs["NX_class"] = "NXmonochromator" + crystal = monochromator.create_group(name="crystal") + crystal.attrs["NX_class"] = "NXcrystal" + + # Create a dataset + chemical_formular = crystal.create_dataset(name="chemical_formular", data="Si") + chemical_formular.attrs["NX_class"] = "NX_CHAR" + + reflection = crystal.create_soft_link( + name="reflection", + target="/entry/collection/devices/mo1_bragg/mo1_bragg_crystal_current_xtal_string/value", + ) + reflection.attrs["NX_class"] = "NX_CHAR" + + # Create a softlink + d_spacing = crystal.create_soft_link( + name="d_spacing", + target="/entry/collection/devices/mo1_bragg/mo1_bragg_crystal_current_d_spacing/value", + ) + d_spacing.attrs["NX_class"] = "NX_FLOAT" + d_spacing.attrs["units"] = "angstrom" + + bragg_offset = crystal.create_soft_link( + name="bragg_offset", + target="/entry/collection/devices/mo1_bragg/mo1_bragg_crystal_current_bragg_off/value", + ) + bragg_offset.attrs["NX_class"] = "NX_FLOAT" + bragg_offset.attrs["units"] = "degree" + + ################### + ### cm mirror specific information + #################### + + collimating_mirror = instrument.create_group(name="collimating_mirror") + collimating_mirror.attrs["NX_class"] = "NXmirror" + + cm_substrate_material = collimating_mirror.create_dataset( + name="substrate_material", data="Si" + ) + cm_substrate_material.attrs["NX_class"] = "NX_CHAR" + + #previous error due to space in name field + + if "cm_bnd" in self.device_manager.devices: + cm_bending = collimating_mirror.create_soft_link( + name="sagittal_radius_bender_motor", + target="/entry/collection/devices/cm_bnd/cm_bnd/value", + ) + cm_bending.attrs["NX_class"] = "NX_FLOAT" + cm_bending.attrs["units"] = "steps" + + if "cm_rotx" in self.device_manager.devices: + cm_incidence_angle = collimating_mirror.create_soft_link( + name="incidence_angle", target="/entry/collection/devices/cm_rotx/cm_rotx/value" + ) + cm_incidence_angle.attrs["NX_class"] = "NX_FLOAT" + cm_incidence_angle.attrs["units"] = "mrad" + + if "cm_roty" in self.device_manager.devices: + cm_yaw_angle = collimating_mirror.create_soft_link( + name="yaw_angle", target="/entry/collection/devices/cm_roty/cm_roty/value" + ) + cm_yaw_angle.attrs["NX_class"] = "NX_FLOAT" + cm_yaw_angle.attrs["units"] = "mrad" + + if "cm_rotz" in self.device_manager.devices: + cm_roll_angle = collimating_mirror.create_soft_link( + name="roll_angle", target="/entry/collection/devices/cm_rotz/cm_rotz/value" + ) + cm_roll_angle.attrs["NX_class"] = "NX_FLOAT" + cm_roll_angle.attrs["units"] = "mrad" + + if 'cm_trx' in self.device_manager.devices: + cm_trx = - self.device_manager.devices.cm_trx.read(cached=True).get('cm_trx').get('value') + stripe = 'Unknown' + for name, low, high in zip(bl.cm.surface, bl.cm.limOptX[0], bl.cm.limOptX[1]): + if low <= cm_trx <= high: + stripe = name + cm_stripe = collimating_mirror.create_dataset( + name="stripe", data=stripe + ) + cm_stripe.attrs["NX_class"] = "NX_CHAR" + + ################### + ### fm mirror specific information + #################### + + focusing_mirror = instrument.create_group(name="focusing_mirror") + focusing_mirror.attrs["NX_class"] = "NXmirror" + + fm_substrate_material = focusing_mirror.create_dataset( + name="substrate_material", data="Si" + ) + fm_substrate_material.attrs["NX_class"] = "NX_CHAR" + + if "fm_bnd" in self.device_manager.devices: + fm_bending = focusing_mirror.create_soft_link( + name="sagittal_radius_bender_motor", + target="/entry/collection/devices/fm_bnd/fm_bnd/value", + ) + fm_bending.attrs["NX_class"] = "NX_FLOAT" + fm_bending.attrs["units"] = "steps" + + if "fm_rotx" in self.device_manager.devices: + fm_incidence_angle = focusing_mirror.create_soft_link( + name="incidence_angle", target="/entry/collection/devices/fm_rotx/fm_rotx/value" + ) + fm_incidence_angle.attrs["NX_class"] = "NX_FLOAT" + fm_incidence_angle.attrs["units"] = "mrad" + + if "fm_roty" in self.device_manager.devices: + fm_yaw_angle = focusing_mirror.create_soft_link( + name="yaw_angle", target="/entry/collection/devices/fm_roty/fm_roty/value" + ) + fm_yaw_angle.attrs["NX_class"] = "NX_FLOAT" + fm_yaw_angle.attrs["units"] = "mrad" + + if "fm_rotz" in self.device_manager.devices: + fm_roll_angle = focusing_mirror.create_soft_link( + name="roll_angle", target="/entry/collection/devices/fm_rotz/fm_rotz/value" + ) + fm_roll_angle.attrs["NX_class"] = "NX_FLOAT" + fm_roll_angle.attrs["units"] = "mrad" + + if 'fm_trx' in self.device_manager.devices: + fm_trx = - self.device_manager.devices.fm_trx.read(cached=True).get('fm_trx').get('value') + stripe = 'Unknown' + for name, low, high in zip(bl.fm.surfaceToroid, bl.fm.limOptXToroid[1], bl.fm.limOptXToroid[0]): + if low <= fm_trx <= high: + stripe = name + ' (toroid)' + fm_stripe = focusing_mirror.create_dataset( + name="stripe", data=stripe + ) + fm_stripe.attrs["NX_class"] = "NX_CHAR" + + ################### + ## nidaq specific information + ################### + + ## Logic if device exist + if "nidaq" in self.device_manager.devices: + + #ai_chans_bits = self.device_manager.devices.nidaq.ai_chans.read(cached=True).get("nidaq_ai_chans").get("value") + ai_chans_bits = self.configuration.get("nidaq", {}).get("nidaq_ai_chans", {}).get("value") + ci_chans_bits = self.configuration.get("nidaq", {}).get("nidaq_ci_chans", {}).get("value") + #add_chans_bits = self.device_manager.devices.nidaq.add_chans.read(cached=True).get("nidaq_add_chans").get("value") + add_chans_bits = self.configuration.get("nidaq", {}).get("nidaq_add_chans", {}).get("value") + + measurement_mode = entry.create_group(name="mode") + measurement_mode.attrs["NX_class"] = "NX_CHAR" + + if (int(ci_chans_bits) & 0x7F) != 0: + # Create a dataset + rayspec_sdd_active = measurement_mode.create_group(name="Multi_Element_Partial_Fluorescence_Yield") + me_sdd = rayspec_sdd_active.create_dataset(name="Detector", data="Rayspec 7 element Silicon Drift Detector") + me_sdd.attrs["NX_class"] = "NX_CHAR" + + if (int(ci_chans_bits) & (1<<8)) != 0: + # Create a dataset + ketek_sdd_active = measurement_mode.create_group(name="Single_Element_Partial_Fluorescence_Yield") + se_sdd = ketek_sdd_active.create_dataset(name="Detector", data="Ketex mini single element Silicon Drift Detector") + se_sdd.attrs["NX_class"] = "NX_CHAR" + + if ((int(ai_chans_bits) & (1<<6)) != 0): + # Create a dataset + pips_active = measurement_mode.create_group(name="Total_Flourescence_Yield") + tfy = pips_active.create_dataset(name="Detector", data="Mirion Technologies Partially Depeleted PIPS Detector") + tfy.attrs["NX_class"] = "NX_CHAR" + + if ((int(ai_chans_bits) & (1<<0)) != 0) & ((int(ai_chans_bits) & (1<<2)) != 0): + # Create a dataset + ai0ai2_active = measurement_mode.create_group(name="Sample_Transmission") + sam_trans = ai0ai2_active.create_dataset(name="Detector", data="Ionitec 15 cm gas filled Ionisation Chambers") + sam_trans.attrs["NX_class"] = "NX_CHAR" + + if ((int(ai_chans_bits) & (1<<2)) != 0) & ((int(ai_chans_bits) & (1<<4)) != 0): + # Create a dataset + ai2ai4_active = measurement_mode.create_group(name="Reference_Transmission") + ref_trans = ai2ai4_active.create_dataset(name="Detector", data="Ionitec 15 cm gas filled Ionisation Chambers") + ref_trans.attrs["NX_class"] = "NX_CHAR" + + main_data = entry.create_group(name="data") + main_data.attrs["NX_class"] = "NXdata" + + ################## + ## energy, test whether the signal exists. how to check from config? + ################### + + energy = main_data.create_group(name="energy") + energy.attrs["NX_class"] = "NXdata" + energy.attrs["units"] = "eV" + + main_data.create_soft_link(name="energy", target="/entry/collection/readout_groups/async/nidaq/nidaq_energy/value") + + ################## + ## i0 + ################### + + if (int(ai_chans_bits) & (1<<0)) !=0: + i0 = main_data.create_group(name="i0") + i0.attrs["NX_class"] = "NXdata" + i0.attrs["units"] = "V" + + main_data.create_soft_link(name="i0", target="/entry/collection/readout_groups/async/nidaq/nidaq_ai0_mean/value") + + ################## + ## i1 + ################### + + if (int(ai_chans_bits) & (1<<2)) !=0: + i1 = main_data.create_group(name="i1") + i1.attrs["NX_class"] = "NXdata" + i1.attrs["units"] = "V" + + main_data.create_soft_link(name="i1", target="/entry/collection/readout_groups/async/nidaq/nidaq_ai2_mean/value") + + ################## + ## i2 + ################### + + if (int(ai_chans_bits) & (1<<4)) !=0: + i2 = main_data.create_group(name="i2") + i2.attrs["NX_class"] = "NXdata" + i2.attrs["units"] = "V" + + main_data.create_soft_link(name="i2", target="/entry/collection/readout_groups/async/nidaq/nidaq_ai4_mean/value") + + ################## + ## ci sum + ################### + + if int(ci_chans_bits) > 0: + ci_sum = main_data.create_group(name="Fluorescence_Sum") + ci_sum.attrs["NX_class"] = "NXdata" + ci_sum.attrs["units"] = "counts" + + main_data.create_soft_link(name="Fluorescence_Sum", target="/entry/collection/readout_groups/async/nidaq/nidaq_cisum/value") + + ################## + ## mu sample, test whether the signal exists. how to check from config? + ################### + + if (int(add_chans_bits) & (1<<0)) !=0: + mu_sample = main_data.create_group(name="mu_sample") + mu_sample.attrs["NX_class"] = "NXdata" + + main_data.create_soft_link(name="mu_sample", target="/entry/collection/readout_groups/async/nidaq/nidaq_smpl_abs/value") + + ################## + ## mu reference, test whether the signal exists. how to check from config? + ################### + + if (int(add_chans_bits) & (1<<1)) !=0: + mu_reference = main_data.create_group(name="mu_reference") + mu_reference.attrs["NX_class"] = "NXdata" + + main_data.create_soft_link(name="mu_reference", target="/entry/collection/readout_groups/async/nidaq/nidaq_ref_abs/value") + + + +