2026-05-19

This was the first commissioning beam time of the 2026-2 cycle.

Code
```{python}
#| code-fold: true
## Example environment setup

ENV_NAME = "20260525_nslsii_data_analysis"

## Create new environment
print(f"--- Creating environment with conda-forge ---")
%conda create -n {ENV_NAME} -c conda-forge ipykernel jupyterlab zarr openpyxl ipywidgets widgetsnbextension jupyterlab_widgets -y

## If in a terminal, activate the environment
#conda activate {ENV_NAME}

## Register the kernel
## In terminal, run in the activated environment without the %conda run -n {ENV_NAME} 
print(f"--- Registering Jupyter Kernel ---")
%conda run -n {ENV_NAME} python -m ipykernel install --user --name {ENV_NAME} --display-name "{ENV_NAME}"

## Install pyhyperscattering to access data and perform downstream workflows.
print(f"--- Installing pyhyperscattering ---")
%conda run -n {ENV_NAME} pip install --no-cache-dir pyhyperscattering[bluesky,ui]

print("\n--- Setup Complete! ---")

## After this cell completes, select the appropriate kernel in the Jupyter notebook
```
Code
```{python}
#| code-fold: true
## Imports


## Cannot put a comment on the same line where % is used
%reload_ext autoreload
%autoreload 2
%matplotlib inline

import PyHyperScattering as phs
print(f'Using PyHyper Version: {phs.__version__}')

import sys
print(f"Python version {sys.version}.  Version info. {sys.version_info}")
import os
#from IPython import display
import pathlib
#basePath = pathlib.Path('.').absolute()

## Data storage and manipulation
import numpy as np
import pandas as pd
import xarray as xr ## !pip install xarray==2022.3
import zarr
import json
import math
import copy
import datetime
import inspect
import re
import scipy
import tqdm

## Plotting
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.colors import LogNorm ## For log scaling in imshow
from matplotlib.pyplot import cm
import matplotlib.dates as mdates
import matplotlib as mpl
from matplotlib.colors import ListedColormap,LinearSegmentedColormap
from matplotlib.colors import Normalize
import matplotlib.patches as patches
from matplotlib.path import Path

## Image processing
from PIL import Image
import cv2 ## !pip install opencv-python  ## For some reason, could not install via conda in miniforge prompt
```



IOC migration

During the shutdown, new virtual machines were deployed to run EPICS IOCs. Cameras, detectors, and other instruments were split to separate IOCs to reduce the chances of the softiocs interfering with each other for dissimilar instruments (e.g., a CCD detector and a motor controller).

So far, motor controllers have been moved from xf07id-ioc1 to xf07id1-inst-ioc1. However, detectors such as the Greateyes CCD camera and beam position monitor fluorescence screen cameras are still on the old xf07id-ioc3.

In all cases, many physical hardware needed to be power cycled, associated softiocs needed to be restarted, and motors needed to be re-homed at the start of the 2026-2 cycle in order for the hardware to connect properly to Bluesky. For the new softiocs, the dzdo portion is no longer needed to run the start, stop, and restart commands. However, dzdo is still necessary for the older IOCs.



Software migration

Over the shutdown, various software updates also were performed.

To ensure the latest updates are running on the RSoXS Control computer, queueserver needed to be restarted along with restarting sst-rsoxs-profile-collection. At this point, only the sst-rsoxs-profile-collection and sst-rsoxs codebases are run from editable pip installs, whereas other dependencies are run from tagged versions.



Beam alignment

Description Process variable name Initial values at the start of beam time Final values at by the end of beam time
EPU60 front-end slit h size (inboard-outboard direction) FE:C07A-OP{Slt:34-Ax:X}size 1.5 1.5
EPU60 front-end slit h center FE:C07A-OP{Slt:34-Ax:X}center 0.52 0.52
EPU60 front-end slit v size (up-down direction) FE:C07A-OP{Slt:34-Ax:Y}size 1.5 1.5
EPU60 front-end slit v center FE:C07A-OP{Slt:34-Ax:Y}center 0.35 0.35
FOE pink beam slits 01, outboard XF:07IDA-OP{Slt:01-Ax:O}Mtr.RBV 3 3
FOE pink beam slits 01, inboard XF:07IDA-OP{Slt:01-Ax:I}Mtr.RBV -5 -5
FOE pink beam slits 01, top XF:07IDA-OP{Slt:01-Ax:T}Mtr.RBV 5 5
FOE pink beam slits 01, bottom XF:07IDA-OP{Slt:01-Ax:B}Mtr.RBV -5 -5
Mirror 1 X (inboard-outboard) XF:07IDA-OP{Mir:M1-Ax:X} 1.3 1.3
Mirror 1 Y (up-down) XF:07IDA-OP{Mir:M1-Ax:Y} -18 -18
Mirror 1 Z (upstream-downstream) XF:07IDA-OP{Mir:M1-Ax:Z} 0 0
Mirror 1 Pitch (rotate along up-down axis) XF:07IDA-OP{Mir:M1-Ax:P} 0.57 0.57
Mirror 1 Roll (rotate along upstream-downstream axis) XF:07IDA-OP{Mir:M1-Ax:R} 0 0
Mirror 1 Yaw (rotate along inboard-outboard axis) XF:07IDA-OP{Mir:M1-Ax:Yaw} 0 0
Mirror 3 X XF:07ID1-OP{Mir:M3ABC-Ax:X} 24.2 24.2
Mirror 3 Y XF:07ID1-OP{Mir:M3ABC-Ax:Y} 18 18
Mirror 3 Z XF:07ID1-OP{Mir:M3ABC-Ax:Z} 0 0
Mirror 3 Pitch XF:07ID1-OP{Mir:M3ABC-Ax:P} 7.77 7.78
Mirror 3 Roll XF:07ID1-OP{Mir:M3ABC-Ax:R} 0 0
Mirror 3 Yaw XF:07ID1-OP{Mir:M3ABC-Ax:Yaw} 0 0
DM6 slits 10, outboard XF:07ID2-OP{Slt:10-Ax:O} - 9
DM6 slits 10, inboard XF:07ID2-OP{Slt:10-Ax:I} - -9
DM6 slits 10, inboard XF:07ID2-OP{Slt:10-Ax:T} - 9
DM6 slits 10, inboard XF:07ID2-OP{Slt:10-Ax:B} - -9
DM6 fluorescence screen XF:07ID2-BI{Diag:06-Ax:Y}Mtr - 80

The beam is running at 500 mA this cycle.

The FOE slits encountered multiple ampfaults even after multiple power cycles. These ampfaults were resovled by resetting (disconnecting and reconnecting) the “disable” key that is located on the back side of the MC03 motor controller.

The DM6 slits 10 were found to be closed at the start of the beam time, so they were opened (v and h centers are 0, and v and h sizes are 18), and the fluorescence screen was moved out of the beam path. Notably, even with the DM7 fully down into the beam path, beam was able to pass through to the I0_up HOPG calibrant. This may be due to beam passing over the fluorescence screen.

Scan IDs 110992-111006 were used to align the beamstop on the WAXS camera and make minor adjustments in slits positions.



Workaround: M2 following errors

Since late November 2025, the M2 mirror has encountered following errors during certain energy moves. Generally, the failure rate was higher while moving downward in energy (moving to smaller angles) than moving upward. The leadscrew was re-lubricated at this time, but it did not reduce the following errors. The MC07 motor controller was power cycled (by switching off/on the three left-most switches) and the softioc was restarted. In the shorter term, following errors were reduced.


During the 2026-1 cycle, the following errors returned. The errors occurred more frequently on the RSoXS 250 grating than on the 1200 l/mm grating, possibly due to the specific angles used for the RSoXS 250 grating. Reducing the mirror and grating velocities from the default 0.1 to 0.05 eliminated the following errors during this cycle.


At the start of the 2026-2 cycle, the following errors returned, and reducing the velocity as low as 0.01 did not change the failure rate.



Sample position calibration

The same sample bar that was used on 2025-11-23 was used for this beam time. The following guess values were run to locate the fiducials.

RE(find_fiducials(f2=[-2.5, 1.26653, 1.4, -2.294175], y2=3.63293, f1=[-1.95, 0.15, 0.85, -1.5],y1=-187.08547))

All maxima were found correctly during the automated scan, as shown below.

Code
```{python}
#| code-fold: true
def view_fiducial_scans(
    scan_id_start,
    maxima = np.full(10, np.nan),
    photodiode = "WAXS Beamstop",
):
    """
    View fiducial scans and optionally the calculated peak maxima.

    Args:
        scan_id_start: int
            Scan ID for the first fiducial scan in the scan series.
        maxima: list of 10 float values
            Optional list of peak maxima found by Bluesky that can be overlaid onto the fiducial scan plots.
            If no list is provided, default is a list of nan, which does not show up on the plot.

    Returns: 
        Plots of each fiducial scan.

    Raises:

    Examples:
    """

    x_lookup = [
                "solid_sample_y",
                "solid_sample_x",
                "RSoXS Sample Up-Down",
                "RSoXS Sample Outboard-Inboard",
    ]
    y_lookup = [
                "DM7 photodiode",
                #"WAXS Beamstop",
                #"SAXS Beamstop",
    ]
    ## TODO: come up with more robust way to handle this
    y_lookup = [photodiode]
    
    subplot_title_labels = np.array([" \n y2 ", " \n x2 at -90° ", " \n x2 at 0° ", " \n x2 at 90° ", " \n x2 at 180° ",
                               " \n y1 ", " \n x1 at -90° ", " \n x1 at 0° ", " \n x1 at 90° ", " \n x1 at 180° "])

    ## Make figure
    number_rows, number_columns = 2, 5
    fig, axs = plt.subplots(number_rows, number_columns, figsize=(number_columns*3.25, number_rows*3.25), edgecolor=(0, 0, 0, 0), linewidth=3); #figsize=(3.25, 3.25) for figure
    #fig.suptitle((""), color=(0, 0, 0, 1), fontname="Calibri", size=24)
    ## Enxure axs always stays a 2D array
    if number_rows == 1 and number_columns == 1: axs = np.array([[axs]])
    if number_rows == 1 and number_columns > 1: axs = axs.reshape(1, number_columns)
    if number_rows > 1 and number_columns == 1: axs = axs.reshape(number_rows, 1)

    ## Fiducial scan series has 10 scans
    scan_ids = np.arange(scan_id_start, (scan_id_start + 10), 1)
    for index_scan_id, scan_id in enumerate(scan_ids):
        ## Load scan.  If the scan does not exist yet, stop the loop.
        try: scan_raw = catalog[int(scan_id)]
        except: break

        ## Gather x and y data
        data_variable_names = list(scan_raw["primary"]["data"].read().data_vars.keys())
        x_axis_name, y_axis_name = "", ""
        for data_variable_name in data_variable_names:
            if data_variable_name in x_lookup:
                x_axis_name = data_variable_name
            if data_variable_name in y_lookup:
                y_axis_name = data_variable_name

        ## Plot
        ax = axs.flatten()[index_scan_id]
        ax.set_title(("Scan ID = " + str(scan_id) + subplot_title_labels[index_scan_id]), color=(0, 0, 0, 1), fontname="Calibri", size=12)
        ax.plot(scan_raw["primary"]["data"][x_axis_name].read(), scan_raw["primary"]["data"][y_axis_name].read(), label="", marker=".", markersize=0, color=(0, 0, 0, 1), linestyle="solid")
        ax.axvline(maxima[index_scan_id], color=(0, 0.7, 0, 1), linestyle="dashed")
        ax.set_xlabel(x_axis_name, color=(0, 0, 0, 1), size=12)
        ax.set_ylabel(y_axis_name, color=(0, 0, 0, 1), size=12)

    ## Plot Formatting
    for index_row in np.arange(0, number_rows, 1):
        for index_column in np.arange(0, number_columns, 1):
            ax = axs[index_row, index_column]
            ## Axes scaling and ranges
            ax.set_xscale("linear")
            ax.set_yscale("linear")
            ## Border formatting
            for Border in np.array(["top", "bottom", "left", "right"]):
                ax.spines[Border].set_linewidth(2) ## axes/border linewidths
                ax.spines[Border].set_color((0, 0, 0, 1)) ## axes/border colors
            for Axis in np.array(["x", "y"]): ax.tick_params(axis=Axis,colors=(0, 0, 0, 1), width=2)
    plt.tight_layout() ## Ensures that subplots don't overlap
    plt.show()






view_fiducial_scans(
    scan_id_start = 111007,
    maxima = [3.218824999999981, -2.8042000000000087, 0.7505399999999938, 1.2411399999999944, -2.321355000000011, -187.27430500000003, -2.0051250000000067, 0.10652499999999065, 0.7922649999999933, -1.3923850000000115], ## From automated fiducial scan
)
```



TetrAMM commissioning

Jira ticket: https://jira.nsls2.bnl.gov/browse/SPEC-16