Skip to content

Commit

Permalink
Merge pull request #2 from ARPES-ASTRID/gui
Browse files Browse the repository at this point in the history
GUI development not complete, but changes to asyncscanner and main were applied in beamtime.
  • Loading branch information
steinnymir authored Jan 17, 2024
2 parents 24525a8 + b0e6b69 commit 2da2762
Show file tree
Hide file tree
Showing 12 changed files with 1,590 additions and 46 deletions.
90 changes: 64 additions & 26 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,56 @@
""" Main file to run the asyncscanner. """
import os
import asyncio
import sys
import logging
import argparse
import datetime
import numpy as np

import yaml
from zipp import Path
from smartscan import AsyncScanManager

from pathlib import Path
from smartscan.utils import ColoredFormatter

if __name__ == "__main__":
def main_asyncio(settings) -> None:
import asyncio
from smartscan import AsyncScanManager

print("Running asyncscanner...")
# an unsafe, unsupported, undocumented workaround :
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
# init scan manager
scan_manager = AsyncScanManager(settings=parsed_args.settings, logger=logger)
# start scan manager
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(scan_manager.start())
except KeyboardInterrupt:
scan_manager.remote.END()
logger.error('Terminated scan from keyboard')
loop.close()
logger.info("Scan manager stopped.")

def main_gui(settings) -> None:
from smartscan.gui import SmartScanApp

app = SmartScanApp(
sys.argv,
settings=settings,
)

try:
app.exec_()
except:
print('exiting')
# sys.exit(app.exec_())

if __name__ == "__main__":

parser = argparse.ArgumentParser(description="AsyncScanManager")
parser.add_argument("--host", type=str, default="localhost", help="SGM4 host")
parser.add_argument("--port", type=int, default=54333, help="SGM4 port")
parser.add_argument(
"--settings", type=str, default="scan_settings.yaml", help="Settings file"
)
parser.add_argument(
"--gui", default=False, action="store_true", help="Start as GUI"
)
# parser.add_argument('--loglevel', type=str, default='DEBUG', help='Log level')
# parser.add_argument('--logdir', type=str, default=None, help='Log directory')
# parser.add_argument('--duration', type=int, default=None, help='Duration of the scan in seconds')
Expand All @@ -31,20 +59,21 @@
parsed_args = parser.parse_args()

# load settings from json file
with open(parsed_args.settings) as f:
settings_file = parsed_args.settings
with open(settings_file) as f:
settings = yaml.load(f, Loader=yaml.FullLoader)

# init logger
logger = logging.getLogger("async_scan_manager")
logger.setLevel(settings["logging"]["level"])
# logger.setLevel("DEBUG")#settings["logging"]["level"])
logging.root.setLevel(settings["logging"]["level"])
formatter = ColoredFormatter(settings["logging"]["formatter"])

sh = logging.StreamHandler()
sh.setLevel(settings["logging"]["level"])
sh.setFormatter(formatter)
logger.addHandler(sh)
logging.root.addHandler(sh)

# numpy compact printing
np.set_printoptions(precision=3, suppress=True)
logger = logging.getLogger(__name__)

if settings["logging"]["directory"] is not None:
logdir = Path(settings["logging"]["directory"])
Expand All @@ -65,16 +94,25 @@
fh.setFormatter(formatter)
logger.addHandler(fh)

print("init asyncscanner object")
# init scan manager
scan_manager = AsyncScanManager(settings=parsed_args.settings, logger=logger)
logger.info('Created logger at level %s' % logger.level)

# start scan manager
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(scan_manager.start())
except KeyboardInterrupt:
scan_manager.remote.END()
logger.error('Terminated scan from keyboard')
loop.close()
logger.info("Scan manager stopped.")
logger.critical('Critical enabled')
logger.error('Error enabled')
logger.warning('Warning enabled')
logger.info('Info enabled')
logger.debug('Debug enabled')

# suppress user warnings
import warnings
warnings.simplefilter('ignore', UserWarning)

# numpy compact printing
np.set_printoptions(precision=3, suppress=True)

# an unsafe, unsupported, undocumented workaround :(
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

if parsed_args.gui:
main_gui(settings=settings_file)
else:
main_asyncio(settings=settings_file)
17 changes: 13 additions & 4 deletions scan_settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ TCP:
logging:
level: INFO
directory: null
formatter: '%(asctime)s - %(name)s - %(levelname)s | %(message)s'
formatter: '%(asctime)s - %(name)-20s | %(levelname)-8s | %(message)s'

paths:
data: './data'

core: # in milliseconds
n_threads: 4
master_clock: 500 # ms
fetch_data_clock: 50 # ms
# process_data_clock: 50 # ms

plots:
posterior_map_shape: [50,50] #full #"auto"

scanning:
max_points: 499 # 0 for unlimited
duration: 1_000_000
Expand All @@ -30,8 +39,8 @@ preprocessing:
roi:
function: roi # name of the function found in gp/preprocessing.py
params:
x_lim: [0,-1] # parameters of the function
y_lim: [0,-1]
x_lim: [75,175] # parameters of the function
y_lim: [10,-10]

tasks: # list of tasks to which to reduce the data
mean: # name of the task found in gp/tasks.py
Expand All @@ -49,7 +58,7 @@ tasks: # list of tasks to which to reduce the data
acquisition_function:
function: acquisition_function_nd # name of the function found in gp/acquisition_functions.py
params:
a: 3.0 # exploration vs exploitation, i.e. variance weight
a: 0.2 # exploration vs exploitation, i.e. variance weight
weights: null
norm: 1 # normalization factor for acquisition function
c: 0 # covariance weight
Expand Down
4 changes: 3 additions & 1 deletion smartscan/asyncscanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,8 @@ async def gp_loop(self) -> None:
)
self._should_stop = True
break
self.gp.cost_function_parameters.update({'prev_points': self.gp.x_data,})
if self.gp.cost_function_parameters is not None:
self.gp.cost_function_parameters.update({'prev_points': self.gp.x_data,})
answer = self.gp.ask(
acquisition_function=aqf,
x0 = missing_positions,
Expand Down Expand Up @@ -570,6 +571,7 @@ async def plotting_loop(self) -> None:
val=np.asarray(self.values),
old_aqf=aqf,
last_spectrum=self.last_spectrum,
settings=self.settings,
)
plt.pause(0.01)
else:
Expand Down
33 changes: 20 additions & 13 deletions smartscan/gp/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ def plot_acqui_f(
fig,
pos,
val,
shape=(50,50),
shape=None,
old_aqf = None,
last_spectrum = None,
settings = None,
):
""" Plot the acquisition function of a GP
Expand All @@ -39,7 +40,8 @@ def plot_acqui_f(

positions, values = gp.x_data, gp.y_data


if shape is None:
shape = settings["plots"]["posterior_map_shape"]
x_pred_0 = np.empty((np.prod(shape),3))
x_pred_1 = np.empty((np.prod(shape),3))
counter = 0
Expand Down Expand Up @@ -74,10 +76,12 @@ def plot_acqui_f(
PV1 = np.reshape(gp.posterior_covariance(x_pred_1)["v(x)"],shape)
sPV1 = np.sqrt(PV1)

a = 1.0
norm = 2.0

aqf = a * np.sqrt(PV0+PV1) + norm * (PM0 + PM1)
a = settings["acquisition_function"]["params"]["a"]
norm = settings["acquisition_function"]["params"]["norm"]
w = settings["acquisition_function"]["params"]["weights"]
if w is None:
w = (1,1)
aqf = norm * (a * np.sqrt(w[0]*PV0+w[1]*PV1) +(w[0]*PM0 + w[1]*PM1))
aqf = np.rot90(aqf,k=-1)[:,::-1]

# fig,ax=plt.subplots(3,1,figsize=(8,6))
Expand Down Expand Up @@ -114,18 +118,19 @@ def plot_acqui_f(
PM /= pmmax
PV /= pvmax

ax[i,0].imshow(PM, clim=[0,1], extent=[*lim_x,*lim_y], origin='lower')
ax[i,0].imshow(PM, clim=[0,1], extent=[*lim_x,*lim_y], origin='lower', aspect="equal")
ax[i,0].set_title(f'PM: {pmmax:.3f}')
ax[i,1].imshow(PV, clim=[0,1], extent=[*lim_x,*lim_y], origin='lower')
ax[i,1].set_title(f'PV: {pvmax:.3f}')
ax[i,1].imshow(PV, clim=[0,1], extent=[*lim_x,*lim_y], origin='lower', aspect="equal")
ax[i,1].set_title(f'PV: {a * np.sqrt(pvmax):.3f}')

ax[i,0].scatter(positions[:,0],positions[:,1], s=20,c='r')
ax[i,1].scatter(positions[:,0],positions[:,1], s=20,c='r')
ax[i,0].scatter(positions[-1,0],positions[-1,1], s=30,c='white')
ax[i,1].scatter(positions[-1,0],positions[-1,1], s=30,c='white')


ax[0,2].imshow(np.zeros_like(PM), clim=[0,1], extent=[*lim_x,*lim_y], origin='lower', aspect="equal")
ax[0,2].scatter(pos[:,0],pos[:,1],s = 25, c=val[:,0],cmap='viridis', marker='o')
ax[1,2].imshow(np.zeros_like(PM), clim=[0,1], extent=[*lim_x,*lim_y], origin='lower', aspect="equal")
ax[1,2].scatter(pos[:,0],pos[:,1],s = 25, c=val[:,1],cmap='viridis', marker='o')
ax[0,2].scatter(pos[-1,0],pos[-1,1],s = 25, c='r', marker='o')
ax[1,2].scatter(pos[-1,0],pos[-1,1],s = 25, c='r', marker='o')
Expand All @@ -135,7 +140,8 @@ def plot_acqui_f(
aqf,
extent=[*lim_x,*lim_y],
origin='lower',
clim=np.quantile(aqf,(0.01,0.99))
clim=np.quantile(aqf,(0.01,0.99)),
aspect="equal",
)
if old_aqf is not None:
diff = old_aqf - aqf
Expand All @@ -144,10 +150,11 @@ def plot_acqui_f(
diff,
extent=[*lim_x,*lim_y],
origin='lower',
cmap='bwr'
cmap='bwr',
aspect="equal"
)
if last_spectrum is not None:
ax[2,2].imshow(last_spectrum, clim=np.quantile(last_spectrum,(0.02,0.98)), origin='lower', cmap='terrain')
ax[2,2].imshow(last_spectrum, clim=np.quantile(last_spectrum,(0.02,0.98)), origin='lower', cmap='terrain', aspect="equal")
# ax[i,0].figure.canvas.draw()
# ax[i,1].figure.canvas.draw()

Expand Down
3 changes: 3 additions & 0 deletions smartscan/gui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .gui import SmartScanApp

__all__ = ["SmartScanApp"]
Loading

0 comments on commit 2da2762

Please sign in to comment.