wip: digital twin
CI for debye_bec / test (push) Failing after 1m4s
CI for debye_bec / test (pull_request) Failing after 1m0s

This commit is contained in:
x01da
2026-05-06 12:58:45 +02:00
parent 3d2485aea7
commit acc5e320cf
2 changed files with 139 additions and 51 deletions
@@ -24,6 +24,9 @@ def calc_surfaces(cfg):
index = bl.cm.surface.index(cfg['cm_stripe'])
cen = (bl.cm.limOptX[0][index] + bl.cm.limOptX[1][index]) / 2
if cfg['cm_trx'] is not None:
cen = cfg['cm_trx']
out['cm']['x'] = [cen-w1/2, cen-w2/2, cen+w2/2, cen+w1/2]
out['cm']['y'] = [-l/2, l/2, l/2, -l/2]
@@ -70,6 +73,8 @@ def calc_surfaces(cfg):
index = surface.index(stripe)
off = (bl.fm.limOptXFlat[0][index] + bl.fm.limOptXFlat[1][index]) / 2
r = bl.fm.r[index]
if cfg['fm_trx'] is not None:
off = cfg['fm_trx']
widthBeam = 2 * bl.fm.center[1] * np.tan(cfg['h_acc'])
@@ -148,9 +148,8 @@ class DigitalTwin(BECWidget, QWidget):
def calc_reality(self):
config = self.get_reality_config()
beam = calc_sideview(config)
self.sideview_plot.data['reality']['x'] = beam['Z']
self.sideview_plot.data['reality']['y'] = beam['Y']
self.sideview_plot.update_curves()
data = {'x': beam['Z'], 'y': beam['Y']}
self.sideview_plot.update_curves('reality', data)
surfaces = calc_surfaces(config)
self.surface_plots.update_surfaces(scene='reality', data=surfaces)
@@ -233,11 +232,13 @@ class DigitalTwin(BECWidget, QWidget):
'v_acc' : self.input.sldi_vacc.value() * 1e-3,
'cm_pitch' : -self.input.cm_pitch.value() * 1e-3,
'cm_stripe' : self.input.cm_stripe.currentText(),
'cm_trx' : None,
'mo1_mode' : self.input.mo1_mode.currentText(),
'mo1_xtal' : self.input.mo1_xtal.currentText(),
'mo1_bragg' : self.bragg_angle,
'fm_pitch' : -self.input.fm_pitch.value() * 1e-3,
'fm_stripe' : self.input.fm_stripe.currentText(),
'fm_trx' : None,
'fm_gain_height' : 1,
'smpl' : self.input.smpl.value(),
}
@@ -277,11 +278,13 @@ class DigitalTwin(BECWidget, QWidget):
'v_acc' : v_acc,
'cm_pitch' : cm_pitch,
'cm_stripe' : cm_stripe,
'cm_trx' : cm_trx,
'mo1_mode' : mo1_mode,
'mo1_xtal' : mo1_bragg['mo1_bragg_crystal_current_xtal_string']['value'],
'mo1_bragg' : mo1_bragg['mo1_bragg_angle']['value']/180*np.pi,
'fm_pitch' : fm_pitch_real,
'fm_stripe' : fm_stripe,
'fm_trx' : fm_trx,
'fm_gain_height' : 1,
'smpl' : self.dev.ot_es1_trz.read()['ot_es1_trz']['value'],
}
@@ -291,9 +294,8 @@ class DigitalTwin(BECWidget, QWidget):
@SafeSlot()
def calc_assistant_sideview(self):
beam = calc_sideview(self.get_assistant_config())
self.sideview_plot.data['assistant']['x'] = beam['Z']
self.sideview_plot.data['assistant']['y'] = beam['Y']
self.sideview_plot.update_curves()
data = {'x': beam['Z'], 'y': beam['Y']}
self.sideview_plot.update_curves('assistant', data)
@SafeSlot()
def calc_assistant_surfaces(self):
@@ -453,8 +455,8 @@ class InputPanel(QWidget):
# Focusing Mirror
self.fm_stripe = ComboBox('fm_stripe', 'Stripe', ['Rh (toroid)', 'Rh (flat)', 'Pt (toroid)', 'Pt (flat)'])
self.fm_pitch_ideal = NumberIndicator('Ideal Pitch', 'mrad', decimals=3)
self.fm_pitch = InputNumberField('fm_pitch', 'Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-10, hl=2)
self.fm_pitch_ideal = NumberIndicator('Ideal Incidence Angle', 'mrad', decimals=3)
self.fm_pitch = InputNumberField('fm_pitch', 'Incidence Angle [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-10, hl=2)
self.fm_refl = NumberIndicator('Reflectivity at x eV', '%', decimals=0)
self.fm_refl_harm = NumberIndicator('Reflectivity at x eV', '%', decimals=0)
self.fm_ass_group = Group(
@@ -656,11 +658,11 @@ class MoverPanel(QWidget):
self._layout .addWidget(self.mover_group)
self._layout .addStretch()
class SurfacePlots(QWidget):
class SurfacePlots(BECWidget, QWidget):
"""Plot widget with two curves and legend."""
def __init__(self, parent=None):
super().__init__(parent)
def __init__(self, parent=None, *arg, **kwargs):
super().__init__(parent=parent, theme_update=True, *arg, **kwargs)
self._layout = QHBoxLayout(self)
self.surfaces = {
@@ -685,16 +687,9 @@ class SurfacePlots(QWidget):
'fm': {},
}
app = QApplication.instance()
theme = app.theme.theme # type: ignore
if theme == "light":
self.color_impenetrable = (30, 30, 30)
self.colors = [(79, 163, 224), (240, 128, 60)]
self.text_color = (255, 255, 255)
else: # dark theme
self.color_impenetrable = (220, 220, 220)
self.colors = [(26, 111, 173), (212, 83, 10)]
self.text_color = (0, 0, 0)
self.color_impenetrable = (0, 0, 0)
self.colors = [(255, 255, 0), (255, 0, 255)]
self.text_color = (255, 255, 255)
# Create plot widgets
for name, widget in self.plots.items():
@@ -739,8 +734,57 @@ class SurfacePlots(QWidget):
)
self.plots[name][scene].setZValue(z_value)
self.walls = []
self.texts = []
self.plot_walls()
self.apply_theme()
def apply_theme(self, theme=None):
if theme is None:
app = QApplication.instance()
theme = app.theme.theme # type: ignore
bg_color = pg.getConfigOption("background")
fg_color = pg.getConfigOption("foreground")
for _, plot in self.plots.items():
# Background
plot['widget'].setBackground(bg_color)
# Axes (tick marks, tick labels, axis line)
for axis in ["left", "bottom", "right", "top"]:
ax = plot['widget'].getAxis(axis)
ax.setPen(pg.mkPen(color=fg_color))
ax.setTextPen(pg.mkPen(color=fg_color))
if theme == "light":
self.color_impenetrable = (30, 30, 30)
self.colors = [(79, 163, 224), (240, 128, 60)]
self.text_color = (255, 255, 255)
else: # dark theme
self.color_impenetrable = (220, 220, 220)
self.colors = [(26, 111, 173), (212, 83, 10)]
self.text_color = (0, 0, 0)
for idx, scene in enumerate(self.surfaces):
for name, device in self.surfaces[scene].items():
if scene in 'assistant':
brush = QBrush(QColor(*self.colors[idx], 255), Qt.DiagCrossPattern)
pen = pg.mkPen(QColor(*self.colors[idx], 255), width=1, style=Qt.DashLine)
else:
brush = QBrush(QColor(*self.colors[idx], 255))
pen = pg.mkPen(QColor(*self.colors[idx], 255), width=1)
self.plots[name][scene].setPen(pen)
self.plots[name][scene].setBrush(brush)
for wall in self.walls:
wall.setPen(pg.mkPen(color=self.color_impenetrable, width=2))
wall.setBrush(pg.QtGui.QBrush(pg.QtGui.QColor(*self.color_impenetrable))) # pylint: disable=E1101
for text in self.texts:
text.setColor(self.text_color)
def plot_walls(self):
def plot_mirror_stripe(widget, surface, limOptX, limOptY):
@@ -758,6 +802,8 @@ class SurfacePlots(QWidget):
widget.addItem(text)
text.setPos((hx+lx)/2, (hy+ly)/2)
text.setZValue(10)
self.walls.append(rect)
self.texts.append(text)
def plot_mono_surface(widget, xtal, xtalWidth, xtalOffsetX, xtalLength):
for sf, w, offx, len in zip(xtal, xtalWidth, xtalOffsetX, xtalLength):
@@ -774,6 +820,8 @@ class SurfacePlots(QWidget):
widget.addItem(text)
text.setPos(offx, 0)
text.setZValue(10)
self.walls.append(rect)
self.texts.append(text)
for name, plot in self.plots.items():
if name in 'cm':
@@ -798,11 +846,11 @@ class SurfacePlots(QWidget):
y = np.array(device['y'] + [device['y'][0]]) if len(device['y']) != 0 else np.array([])
plot.setData(x=x, y=y)
class SideviewPlot(QWidget):
class SideviewPlot(BECWidget, QWidget):
"""Plot widget with two curves and legend."""
def __init__(self, parent=None):
super().__init__(parent)
def __init__(self, parent=None, *arg, **kwargs):
super().__init__(parent=parent, theme_update=True, *arg, **kwargs)
self._layout = QVBoxLayout(self)
# self._layout.setSizeConstraint(QLayout.SetFixedSize) # type: ignore
@@ -810,36 +858,29 @@ class SideviewPlot(QWidget):
self.plot_widget.getAxis('bottom').enableAutoSIPrefix(False)
self.plot_widget.addLegend()
app = QApplication.instance()
theme = app.theme.theme # type: ignore
if theme == "light":
self.color_impenetrable = (30, 30, 30)
self.colors = [(26, 111, 173), (212, 83, 10)]
else: # dark theme
self.color_impenetrable = (220, 220, 220)
self.colors = [(79, 163, 224), (240, 128, 60)]
self.curves = []
self.color_impenetrable = (0, 0, 0)
self.colors = [(255, 255, 0), (255, 0, 255)]
self.data = {
'assistant': {'x': [0, 1000, 2000], 'y': [0, 20, 30]},
'reality': {'x': [0, 1000, 2000], 'y': [0, 15, 50]},
}
self.plots = {}
self.pipes = []
self.walls = []
for idx, name in enumerate(self.data.keys()):
if name in "assistant":
for idx, scene in enumerate(self.data.keys()):
if scene in "assistant":
pen = pg.mkPen(color=self.colors[idx], width=2, style=Qt.DashLine)
else:
pen = pg.mkPen(color=self.colors[idx], width=2)
self.curves.append(
self.plot_widget.plot(
[],
[],
pen=pen,
name=name,
)
self.plots[scene] = self.plot_widget.plot(
[],
[],
pen=pen,
name=scene,
)
self.plot_group = Group(
@@ -862,7 +903,50 @@ class SideviewPlot(QWidget):
self.plot_vacuum_pipes()
self.plot_walls()
self.update_curves()
self.apply_theme()
def apply_theme(self, theme=None):
if theme is None:
app = QApplication.instance()
theme = app.theme.theme # type: ignore
bg_color = pg.getConfigOption("background")
fg_color = pg.getConfigOption("foreground")
# Background
self.plot_widget.setBackground(bg_color)
# Axes (tick marks, tick labels, axis line)
for axis in ["left", "bottom", "right", "top"]:
ax = self.plot_widget.getAxis(axis)
ax.setPen(pg.mkPen(color=fg_color))
ax.setTextPen(pg.mkPen(color=fg_color))
if theme == "light":
self.color_impenetrable = (30, 30, 30)
self.colors = [(79, 163, 224), (240, 128, 60)]
self.text_color = (255, 255, 255)
else: # dark theme
self.color_impenetrable = (220, 220, 220)
self.colors = [(26, 111, 173), (212, 83, 10)]
self.text_color = (0, 0, 0)
for idx, scene in enumerate(self.data):
if scene in 'assistant':
brush = QBrush(QColor(*self.colors[idx], 255), Qt.DiagCrossPattern)
pen = pg.mkPen(QColor(*self.colors[idx], 255), width=1, style=Qt.DashLine)
else:
brush = QBrush(QColor(*self.colors[idx], 255))
pen = pg.mkPen(QColor(*self.colors[idx], 255), width=1)
self.plots[scene].setPen(pen)
self.plots[scene].setBrush(brush)
for wall in self.walls:
wall.setPen(pg.mkPen(color=self.color_impenetrable, width=2))
wall.setBrush(pg.QtGui.QBrush(pg.QtGui.QColor(*self.color_impenetrable))) # pylint: disable=E1101
for pipe in self.pipes:
pipe.setPen(pg.mkPen(color=self.color_impenetrable, width=2))
def plot_vacuum_pipes(self):
for i, _ in enumerate(bl.vacuum_pipes.center):
@@ -890,13 +974,12 @@ class SideviewPlot(QWidget):
rect.setBrush(pg.QtGui.QBrush(pg.QtGui.QColor(*self.color_impenetrable))) # pylint: disable=E1101
rect.setPen(pg.mkPen(color=self.color_impenetrable, width=2))
self.plot_widget.addItem(rect)
self.walls.append(rect)
def update_curves(self):
for idx, element in enumerate(self.data):
self.curves[idx].setData(
x=np.array(self.data[element]['x']),
y=np.array(self.data[element]['y']),
)
def update_curves(self, scene, data):
self.data[scene] = data
plot = self.plots[scene]
plot.setData(x=self.data[scene]['x'], y=self.data[scene]['y'])
if __name__ == "__main__":
@@ -911,4 +994,4 @@ if __name__ == "__main__":
# win.resize(1000, 800)
win.show()
sys.exit(app.exec_())
sys.exit(app.exec_())