This commit is contained in:
@@ -1,23 +1,21 @@
|
||||
# import builtins
|
||||
# import datetime
|
||||
# import os
|
||||
# import subprocess
|
||||
# import time
|
||||
# from pathlib import Path
|
||||
import inspect
|
||||
|
||||
# import numpy as np
|
||||
from bec_lib import bec_logger
|
||||
# from bec_lib.alarm_handler import AlarmBase
|
||||
# from bec_lib.pdf_writer import PDFWriter
|
||||
from typeguard import typechecked
|
||||
|
||||
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.diagnostics import cSAXSDiagnostics
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.filter_transmission import cSAXSFilterTransmission
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.intensity_map_predict_gap import (
|
||||
predict_gap as _predict_gap,
|
||||
)
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.slits import cSAXSSlits
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.smaract import cSAXSInitSmaractStages
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.smaract import cSAXSSmaract
|
||||
from csaxs_bec.bec_ipython_client.plugins.omny.omny_general_tools import OMNYTools
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.filter_transmission import cSAXSFilterTransmission
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.diagnostics import cSAXSDiagnostics
|
||||
from csaxs_bec.bec_ipython_client.plugins.cSAXS.slits import cSAXSSlits
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class cSAXSError(Exception):
|
||||
pass
|
||||
|
||||
@@ -36,6 +34,80 @@ class cSAXS(
|
||||
self.diagnostics = cSAXSDiagnostics()
|
||||
super().__init__(client=client)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Undulator
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def predict_gap(self, energy: float, n: int = 3) -> None:
|
||||
"""Print the predicted undulator gap for *energy* [keV] on harmonic *n*.
|
||||
|
||||
Examples
|
||||
--------
|
||||
csaxs.predict_gap(6.2) # h=3 (default)
|
||||
csaxs.predict_gap(10.0, n=5) # explicit harmonic
|
||||
"""
|
||||
import math
|
||||
|
||||
gap = float(_predict_gap(energy, n=n))
|
||||
if math.isnan(gap):
|
||||
print(f"Energy {energy:.3f} keV is unreachable on harmonic {n}.")
|
||||
else:
|
||||
print(f"Predicted gap for {energy:.3f} keV (h={n}): {gap:.4f} mm")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Help / discovery
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def commands(self) -> None:
|
||||
"""Print a table of all available cSAXS commands and sub-namespaces."""
|
||||
from rich import box
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
entries: list[tuple[str, str]] = []
|
||||
seen: set[str] = set()
|
||||
|
||||
for cls in type(self).__mro__:
|
||||
if cls is object:
|
||||
continue
|
||||
module = getattr(cls, "__module__", "") or ""
|
||||
if "csaxs_bec" not in module:
|
||||
continue
|
||||
for name, func in inspect.getmembers(cls, predicate=inspect.isfunction):
|
||||
if name.startswith("_") or name in seen:
|
||||
continue
|
||||
seen.add(name)
|
||||
doc = (inspect.getdoc(func) or "").split("\n")[0].strip()
|
||||
entries.append((name, doc))
|
||||
|
||||
entries.sort(key=lambda x: x[0])
|
||||
|
||||
tbl = Table(title="cSAXS Commands", box=box.SQUARE, show_lines=False)
|
||||
tbl.add_column("Command", style="cyan bold", no_wrap=True, min_width=46)
|
||||
tbl.add_column("Description")
|
||||
for name, doc in entries:
|
||||
tbl.add_row(f"csaxs.{name}()", doc)
|
||||
console.print(tbl)
|
||||
console.print("")
|
||||
|
||||
ns = Table(title="Sub-namespaces", box=box.SQUARE, show_lines=False)
|
||||
ns.add_column("Access", style="cyan bold", no_wrap=True, min_width=46)
|
||||
ns.add_column("Description")
|
||||
for access, desc in [
|
||||
("csaxs.diagnostics.show_all()", "All diagnostic device readbacks"),
|
||||
(
|
||||
"csaxs.diagnostics.bpm_xbox1 / .bpm_xbox2",
|
||||
"BPM diagnostics — .show_all(), .gain(val)",
|
||||
),
|
||||
("csaxs.diagnostics.bim", "BIM diagnostics — .show_all(), .gain(val)"),
|
||||
("csaxs.diagnostics.beamstop", "Beamstop diode — .show_all(), .gain(val)"),
|
||||
("csaxs.diagnostics.polarization", "Polarization diodes — .show_all(), .gain(val)"),
|
||||
("csaxs.OMNYTools.*", "OMNY instrument tools"),
|
||||
]:
|
||||
ns.add_row(access, desc)
|
||||
console.print(ns)
|
||||
|
||||
|
||||
# this is the csaxs master file that imports all routines from csaxs
|
||||
@@ -45,4 +117,6 @@ class cSAXS(
|
||||
# csaxs = cSAXS(bec)
|
||||
#
|
||||
# then all commands can be accessed by for example
|
||||
# csaxs._cSAXS_smaract_stages_.....
|
||||
# csaxs.commands()
|
||||
# csaxs.predict_gap(6.2)
|
||||
# csaxs._cSAXS_smaract_stages_...
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Undulator gap predictor emitted by plot_intensity_map.py.
|
||||
Edit the fitted constants in the signature to retune."""
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def predict_gap(energy, n=3, gap_min=5.0,
|
||||
E_inf=3.83167, c0=3.1133, c1=-0.644678, c2=0.0210398):
|
||||
"""Undulator gap [mm] to place `energy` [keV] on harmonic `n`.
|
||||
Fitted constants are the defaults below; edit them to retune.
|
||||
Returns NaN where the energy is unreachable on that harmonic."""
|
||||
energy = np.asarray(energy, float)
|
||||
arg = E_inf * n / energy - 1.0 # required K^2/2; must be > 0
|
||||
with np.errstate(invalid="ignore", divide="ignore"):
|
||||
y = np.log(arg)
|
||||
if abs(c2) < 1e-12:
|
||||
g = (y - c0) / c1
|
||||
else:
|
||||
disc = c1 * c1 - 4.0 * c2 * (c0 - y)
|
||||
sq = np.sqrt(np.where(disc >= 0, disc, np.nan))
|
||||
r1 = (-c1 + sq) / (2.0 * c2)
|
||||
r2 = (-c1 - sq) / (2.0 * c2)
|
||||
g = np.where(c1 + 2.0 * c2 * r1 < 0, r1, r2)
|
||||
g = np.where(arg > 0, g, np.nan) # above harmonic cutoff
|
||||
g = np.where(g >= gap_min, g, np.nan) # below mechanical minimum
|
||||
return g
|
||||
Reference in New Issue
Block a user