From 1cee10d034611b6fbcec0a46f4fa47206f304774 Mon Sep 17 00:00:00 2001 From: rskoupy Date: Tue, 28 Nov 2023 11:53:50 +0100 Subject: [PATCH] Version 0.06 --- README.md | 6 + calibrations.xlsx | Bin 0 -> 27092 bytes ptychoScopy.ipynb | 854 +++++++++++++++++++++++++++++++++++++++++++ ptychoscopy_logo.png | Bin 0 -> 16036 bytes 4 files changed, 860 insertions(+) create mode 100644 README.md create mode 100644 calibrations.xlsx create mode 100644 ptychoScopy.ipynb create mode 100644 ptychoscopy_logo.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..24f3781 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# ptychoScopy + +Jupyter based interactive data acquisition tool designed for appropriate ptychographic data collection. It computes nessesary characteristics which play crutial role in final data reconstruction. You can chose of **Direct methods** (mainly Single Side Band ptychography) or **Iterative reconstruction** which takes probe defocus into account. +With this tool, you can check for probe CTF, scanning step size, probe overlap, detector camera length a proper angular range collection, reconstructed probe size and many more. +### Calibrations +Every STEM has its own combination of parameters (magnifications, probe currents, aperture angles, camera lengths or detectors). These have to be taken into account for proper data acquisition design. Before first use, fill your own values into the file **calibrations.xlsx**. Instructions how to fill are in the sheet called HELP. diff --git a/calibrations.xlsx b/calibrations.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..894199ce59204bd3321f7c8dfcd95801cfca8907 GIT binary patch literal 27092 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 Tu#{wA5Cy^$z>^ZcC4+bXo~{s} literal 0 HcmV?d00001 diff --git a/ptychoScopy.ipynb b/ptychoScopy.ipynb new file mode 100644 index 0000000..19b3704 --- /dev/null +++ b/ptychoScopy.ipynb @@ -0,0 +1,854 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "8f22e3c3", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\skoupy_r\\Anaconda3\\lib\\site-packages\\scipy\\__init__.py:155: UserWarning: A NumPy version >=1.18.5 and <1.25.0 is required for this version of SciPy (detected version 1.26.1\n", + " warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion}\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Numpy version 1.26.1\n", + "Matplotlib version 3.8.1\n", + "Pandas version 2.1.2\n", + "Ipywidgets version 7.6.5\n", + "Plotly version 5.9.0\n" + ] + } + ], + "source": [ + "### Initial packages import ###\n", + "import ipywidgets as widgets\n", + "import scipy.constants as cons\n", + "import math\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib\n", + "import plotly.graph_objects as go\n", + "from ipywidgets import interactive, interactive_output, interact, HBox, Layout, VBox, Label\n", + "import copy\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "from IPython.display import display, HTML, Markdown\n", + "\n", + "import plotly\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "print(\"Numpy version \", np.__version__)\n", + "print(\"Matplotlib version \", matplotlib.__version__)\n", + "print(\"Pandas version \", pd.__version__)\n", + "print(\"Ipywidgets version \", widgets.__version__)\n", + "print(\"Plotly version \", plotly.__version__)" + ] + }, + { + "cell_type": "markdown", + "id": "7671c652-c4e7-4f1f-a87e-e4c67bafed51", + "metadata": {}, + "source": [ + "Tested with packages: \\\n", + "Numpy version 1.26.1 \\\n", + "Matplotlib version 3.8.1 \\\n", + "Pandas version 2.1.2 \\\n", + "Ipywidgets version 7.6.5 \\\n", + "Plotly version 5.9.0 " + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "7425242d-3c91-4c1e-a424-08625a38ee7a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "### Function nefinitions ###\n", + "\n", + "calib = r'C:\\Users\\skoupy_r\\--PYTHON\\ptychoScopy\\calibrations.xlsx' # Full path to the calibration file\n", + "\n", + "class interaction: \n", + " \"\"\"Loads calibration settings and ranges from .xlsx file.\n", + " These are than used for widgets creation.\"\"\"\n", + " \n", + "\n", + " def __init__(self):\n", + " self.calib = calib\n", + "\n", + " def apertures(self):\n", + " excel_data = pd.read_excel(self.calib,sheet_name='Probes')\n", + " excel_lin = list((excel_data[excel_data.Probe.isin([\"Probe\"])]))\n", + " apertures = excel_lin[1::]\n", + " return apertures\n", + " \n", + " def detectors(self):\n", + " excel_data = pd.read_excel(self.calib,sheet_name='Detector')\n", + " excel_lin = list((excel_data[excel_data.Type.isin([\"Type\"])]))\n", + " detectors = excel_lin[1::]\n", + " return detectors\n", + " \n", + " def probes(self):\n", + " probes = list(pd.read_excel(self.calib,sheet_name='Probes').Probe)\n", + " probes = probes[3::]\n", + " return probes\n", + " \n", + " def magnifications(self):\n", + " excel_data = pd.read_excel(self.calib,sheet_name='Ranges')\n", + " magnifications = list(excel_data.Magnification)\n", + " return magnifications\n", + " \n", + " def energies(self): \n", + " excel_data = pd.read_excel(self.calib,sheet_name='Ranges')\n", + " energies = [str(e) for e in list(excel_data.BeamEnergy_keV)]\n", + " energies = [x for x in energies if x != 'nan']\n", + " energies = [int(float(e)) for e in energies]\n", + " return energies\n", + " \n", + " def mappings(self): \n", + " excel_data = pd.read_excel(self.calib,sheet_name='Ranges')\n", + " mappings = [str(e) for e in list(excel_data.Mapping)]\n", + " mappings = [x for x in mappings if x != 'nan']\n", + " mappings = [int(float(e)) for e in mappings]\n", + " return mappings\n", + " \n", + " def cameralengths(self): \n", + " cameralengths = list(pd.read_excel(self.calib,sheet_name='Pixel').NominalCL)\n", + " return cameralengths\n", + " \n", + " def cameralengths_eff(self): \n", + " cameralengths_eff = list(pd.read_excel(self.calib,sheet_name='Pixel').DetectorCL_cm)\n", + " return cameralengths_eff\n", + "\n", + " def dwelltimes(self): \n", + " excel_data = pd.read_excel(self.calib,sheet_name='Ranges')\n", + " dwelltimes = [str(e) for e in list(excel_data.DwellTime_us)]\n", + " dwelltimes = [x for x in dwelltimes if x != 'nan']\n", + " dwelltimes = [int(float(e)) for e in dwelltimes] \n", + " return dwelltimes \n", + " \n", + "opt = interaction()\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "class ptychoScopy:\n", + " \"\"\"Computes various characteristics describing used setting.\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.calib = calib\n", + " \n", + " \n", + " \n", + " def get_wavelength(self, beam):\n", + " self.wavelength = cons.h/np.sqrt(2*cons.electron_mass*cons.elementary_charge*beam*1e3*(1+((cons.elementary_charge*beam*1e3)/(2*cons.electron_mass*cons.speed_of_light**2))))\n", + " return self.wavelength\n", + " \n", + " def get_fov(self, mag):\n", + " self.fov = 214620000/(mag*1e6)\n", + " return self.fov\n", + " \n", + " def get_angle(self, aperture):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes',header=0)\n", + " excel_line = excel_data[excel_data.Probe.isin(['ProbeSemi_angle_mrad'])]\n", + " angle = excel_line[aperture]\n", + " self.angle = np.array(angle).item()\n", + " return self.angle\n", + " \n", + " def get_angle_corr(self, aperture):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes',header=0)\n", + " excel_line = excel_data[excel_data.Probe.isin(['CorrectedProbeSemi_angle_mrad'])]\n", + " angle_corr = excel_line[aperture]\n", + " self.angle_corr = np.array(angle_corr).item()\n", + " return self.angle_corr\n", + " \n", + " def get_current(self, probe,aperture):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes',header=0)\n", + " excel_line = excel_data[excel_data.Probe.isin([probe])]\n", + " current = excel_line[aperture]\n", + " self.current = np.array(current).item()\n", + " return self.current\n", + " \n", + " def get_currents_over_probes(self, probe):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes',header=0)\n", + " currents_probes = np.array(excel_data[excel_data.Probe.isin([probe])])\n", + " currents_probes = currents_probes[0]\n", + " currents_probes = currents_probes[1::]\n", + " return currents_probes\n", + "\n", + " def get_currents_over_apertures(self, aperture):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes',header=0)\n", + " currents_apertures = np.array(list(excel_data[aperture]))\n", + " return currents_apertures\n", + "\n", + " def get_pixel_angle(self, cl, camera, binning):\n", + " excel_data = pd.read_excel(calib,sheet_name='Pixel',header=0)\n", + " excel_line = excel_data[excel_data.NominalCL.isin([cl])]\n", + " pixel_angle = excel_line[camera]\n", + " pixel_angle = np.array(pixel_angle).item()\n", + " self.pixel_angle = pixel_angle*binning\n", + " return self.pixel_angle\n", + " \n", + " def get_cl_detector(self,cl):\n", + " excel_data = pd.read_excel(calib,sheet_name='Pixel')\n", + " excel_line = excel_data[excel_data.NominalCL.isin([cl])]\n", + " self.cl_detector = excel_line['DetectorCL_cm']\n", + " self.cl_detector = self.cl_detector.item()\n", + " return self.cl_detector\n", + " \n", + " def get_aq_frec(self, dwell_time):\n", + " self.aq_frec = 1000/dwell_time\n", + " return self.aq_frec \n", + " \n", + " def get_step_size(self, matrix):\n", + " self.step_size = self.fov/(matrix-1)\n", + " return self.step_size \n", + " \n", + " def get_step_correction(self): \n", + " step_correction = [str(e) for e in list(pd.read_excel(calib,sheet_name='Ranges').Step_size_correction)]\n", + " step_correction = np.array([x for x in step_correction if x != 'nan']).astype(float)\n", + " self.step_correction = step_correction.item()\n", + " return self.step_correction\n", + " \n", + " def get_beam_diameter(self, aperture, defocus):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes')\n", + " excel_line = excel_data[excel_data.Probe.isin(['0_defocus_beam_diameter_nm'])]\n", + " beam_0_diameter = np.array([excel_line[aperture]]).astype(float)\n", + " beam_0_diameter = beam_0_diameter.item()\n", + " self.beam_diameter = defocus*2*np.tan(self.angle_corr/1000)+beam_0_diameter\n", + " return self.beam_diameter\n", + " \n", + " def get_0beam_diameter(self, aperture):\n", + " excel_data = pd.read_excel(calib, sheet_name='Probes')\n", + " excel_line = excel_data[excel_data.Probe.isin(['0_defocus_beam_diameter_nm'])]\n", + " beam_0_diameter = np.array([excel_line[aperture]]).astype(float)\n", + " self.beam_0_diameter = beam_0_diameter.item()\n", + " return self.beam_0_diameter\n", + " \n", + " def get_detector(self, camera, binning):\n", + " excel_data = pd.read_excel(calib, sheet_name='Detector')\n", + " excel_line = excel_data[excel_data.Type.isin(['Pixels'])]\n", + " num_pixels = np.array([excel_line[camera]]).astype(float)\n", + " num_pixels = num_pixels.item()\n", + " self.num_pixels = num_pixels/binning\n", + " excel_line = excel_data[excel_data.Type.isin(['Real_size'])]\n", + " size_pixel = excel_line[camera]\n", + " size_pixel = size_pixel.item()\n", + " self.size_pixel = size_pixel*binning\n", + " return self.num_pixels, self.size_pixel \n", + " \n", + " def get_det_resolution(self, camera):\n", + " excel_data = pd.read_excel(calib, sheet_name='Detector')\n", + " excel_line = excel_data[excel_data.Type.isin(['Pixels'])]\n", + " num_pixels = np.array([excel_line[camera]]).astype(float)\n", + " self.num_pixels = num_pixels.item()\n", + " return self.num_pixels\n", + " \n", + " def get_det_orig_pixel_size(self, camera):\n", + " excel_data = pd.read_excel(calib, sheet_name='Detector')\n", + " excel_line = excel_data[excel_data.Type.isin(['Real_size'])]\n", + " orig_size_pixel = excel_line[camera]\n", + " self.orig_size_pixel = orig_size_pixel.item()\n", + " return self.orig_size_pixel\n", + " \n", + " def get_pixel_covers(self,camera, binning): \n", + " excel_data = pd.read_excel(calib, sheet_name='Pixel')\n", + " pixel_covers = list(excel_data[camera])\n", + " pixel_covers = [str(e) for e in pixel_covers]\n", + " pixel_covers = [x for x in pixel_covers if x != 'nan']\n", + " pixel_covers = [float(e) for e in pixel_covers] \n", + " pixel_covers = np.array(pixel_covers)\n", + " self.pixel_covers = pixel_covers * binning\n", + " return self.pixel_covers\n", + "\n", + " def get_pumping_apertures(self): \n", + " excel_data = pd.read_excel(calib,sheet_name='Pixel')\n", + " pump_apert = [str(e) for e in list(excel_data.Pumping_aperture_limitation_mrad)]\n", + " pump_apert = [x for x in pump_apert if x != 'nan']\n", + " self.pump_apert = [float(e) for e in pump_apert]\n", + " return self.pump_apert \n", + " \n", + " def get_ssb_ctf(self):\n", + " ### Contrast transfer function ###\n", + " omega = np.linspace(1e-6,2,100)\n", + " p3 = np.arccos(np.minimum(1,omega))\n", + " p3[np.isnan(p3)] = 0\n", + " p5 = 1-(omega**2)\n", + " p5[p5 <0] = 0\n", + " p4 = omega*np.sqrt(p5)\n", + " p4[np.isnan(p4)] = 0\n", + " pctf = (2/cons.pi)*(np.arccos(omega/2)-p3+p4-(omega/2*np.sqrt(1-(omega/2)**2)))\n", + " return pctf\n", + " \n", + " \n", + " def cl4ssb(self, detector_cover_a_all):\n", + " ssb_cl = copy.deepcopy(detector_cover_a_all)\n", + " ssb_cl[ssb_cl<1] = \"nan\"\n", + " ssb_cl = np.nanmin(ssb_cl)\n", + " kde = np.array(np.where(ssb_cl == detector_cover_a_all)).item()\n", + " kolik = np.array(detector_cover_a_all[kde]) \n", + " cl_all = opt.cameralengths()\n", + " ssb_cl = np.array(cl_all[kde]) # Recommended cl\n", + " return ssb_cl, kolik\n", + " \n", + "\n", + "pty = ptychoScopy() \n", + " \n", + "class interaction:\n", + " \"\"\"Live interaction with parameter computing.\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.calib = calib\n", + " \n", + " \n", + " def show_wavelength(self,caller):\n", + " beam_res.value = f'Wavelength (pm) {str(\"{:.2f}\".format(pty.get_wavelength(caller.new)*1e12))}'\n", + " return beam_res.value\n", + " \n", + " def show_detector(self,caller):\n", + " camera_res.value = f'Detector full pixel array {\"{:.0f}\".format(pty.get_det_resolution(caller.new))}'\n", + " return camera_res.value\n", + " \n", + " def show_pixel_size(self,caller):\n", + " pixel_size_res.value = f'Native pixel size (μm) {\"{:.0f}\".format(pty.get_det_orig_pixel_size(caller.new))}' \n", + " return pixel_size_res.value\n", + " \n", + " def show_probe_angle(self,caller):\n", + " aperture_res.value = f'Probe semi angle (mrad) {\"{:.2f}\".format(pty.get_angle(caller.new))}' \n", + " return aperture_res.value\n", + " \n", + " def show_corrprobe_angle(self,caller):\n", + " aperture_res2.value = f'Corrected semi angle (mrad) {\"{:.2f}\".format(pty.get_angle_corr(caller.new))}'\n", + " return aperture_res2.value\n", + " \n", + " def show_fov(self,caller):\n", + " fov_res.value = f'Field of view (nm) {pty.get_fov(caller.new) }'\n", + " return fov_res.value\n", + " \n", + " def show_frame_rate(self,caller):\n", + " dwell_time_res.value = f'Frame rate (kHz) {pty.get_aq_frec(caller.new)}' \n", + " return dwell_time_res.value\n", + " \n", + " def show_cd_det(self,caller):\n", + " cl_det_res.value = f'Detector camera length (cm) {pty.get_cl_detector(caller.new)}' \n", + " return cl_det_res.value\n", + " \n", + " \n", + "inte = interaction() \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "8055b802-cf83-4250-aea9-54e3e6b73db0", + "metadata": {}, + "outputs": [], + "source": [ + "### CONTROLS ##########################################\n", + "align = dict(layout=widgets.Layout(width='385px') , style = {'description_width': '80px','button_width': \"35px\"}, button_style='primary',disabled=False,)\n", + "align2 = dict(layout=widgets.Layout(width='250px') , style = {'description_width': '100px','button_width': '50px'}, disabled=False,)\n", + "to_right = dict(layout=widgets.Layout(display=\"flex\", justify_content=\"flex-end\", width=\"95%\", ))\n", + "\n", + "### Main controls ###\n", + "beam = widgets.Dropdown(options=opt.energies(),value=200,description='Energy (keV)', **align)\n", + "aperture = widgets.ToggleButtons(options=opt.apertures(), value='30um', description='Aperture', **align, tooltips=['', ''])\n", + "probe = widgets.ToggleButtons(options=opt.probes(), value='8C', description='Probe', **align, tooltips=['', ''])\n", + "defocus = widgets.IntSlider(description='Defocus (nm)', value=0, min=0, max=1000, step=1, continuous_update=False, **align )\n", + "defocus.style.handle_color = 'blue'\n", + "mag = widgets.ToggleButtons(options=opt.magnifications(),value=3,description='Mag. (Mx)', **align) \n", + "matrix = widgets.ToggleButtons(options=opt.mappings(), value=2048, description='Matrix', **align, tooltips=['', ''])\n", + "cl = widgets.ToggleButtons(options=opt.cameralengths(), value=12, description='Nominal (cm)', **align, tooltips=['', ''])\n", + "camera = widgets.Dropdown(options=opt.detectors(), description='Detector', **align, tooltips=['', ''])\n", + "restriction = widgets.ToggleButtons(options=[('NO',False), ('YES',True)], description='Use PAAR',**align, tooltips=['', ''])\n", + "binning = widgets.ToggleButtons(options=[('NO',1), ('2×2',2), ('4×4',4),('6×6',6),('8×8',8)], description='Binning', **align, tooltips=['Full detector resolution', 'Binning 2x2 - detector resolution /2, pixel size *2', 'Binning 4x4'])\n", + "dwell_time = widgets.ToggleButtons(options=opt.dwelltimes(), value=10, description='Dwell time (μs)', **align, tooltips=['', ''])\n", + "\n", + "### Small controls ###\n", + "method = widgets.ToggleButtons(options=[('Direct','direct'), ('Iterative','iterative')], description='Methods', **align2, button_style='primary', tooltips=['SSB', 'PIE, MLc, DM'])\n", + "ssb_freq_length = widgets.RadioButtons(options=['mrad', 'A'], description='CTF-SSB x-axis:', **align2) \n", + "scans = widgets.RadioButtons(options=[4,6,8,10],description='Scan points:',continuous_update=False, **align2)\n", + "\n", + "### Live update ### \n", + "beam_res = widgets.Label(value = f'Wavelength (pm) '+ str(\"{:.2f}\".format(pty.get_wavelength(beam.value)*1e12)), **to_right)\n", + "beam.observe(inte.show_wavelength, names='value')\n", + "camera_res = widgets.Label(value = f'Detector full pixel array '+ str(\"{:.0f}\".format(pty.get_det_resolution(camera.value))), **to_right)\n", + "camera.observe(inte.show_detector, names='value')\n", + "pixel_size_res = widgets.Label(value = f'Native pixel size (μm) '+ str(\"{:.0f}\".format(pty.get_det_orig_pixel_size(camera.value))),**to_right)\n", + "camera.observe(inte.show_pixel_size, names='value')\n", + "aperture_res = widgets.Label(value = f'Probe semi angle (mrad) '+ str(\"{:.2f}\".format(pty.get_angle(aperture.value))), **to_right)\n", + "aperture.observe(inte.show_probe_angle, names='value') \n", + "aperture_res2 = widgets.Label(value = f'Corrected semi angle (mrad) '+ str(\"{:.2f}\".format(pty.get_angle_corr(aperture.value))), **to_right)\n", + "aperture.observe(inte.show_corrprobe_angle, names='value') \n", + "fov_res = widgets.Label(value = f'Field of view (nm) '+ str(pty.get_fov(mag.value)), **to_right)\n", + "mag.observe(inte.show_fov, names='value') \n", + "dwell_time_res = widgets.Label(value = f'Frame rate (kHz) '+ str(pty.get_aq_frec(dwell_time.value)),**to_right)\n", + "dwell_time.observe(inte.show_frame_rate, names='value') \n", + "cl_det_res = widgets.Label(value = f'Detector camera length (cm) '+ str(pty.get_cl_detector(cl.value)),**to_right)\n", + "cl.observe(inte.show_cd_det, names='value') \n", + "\n", + "box_layout = widgets.Layout(border='solid 1px blue',margin='0px 10px 10px 0px', padding='5px 5px 5px 5px')\n", + "col1 = widgets.VBox([widgets.Label('Beam'), beam, aperture, probe, defocus])\n", + "col1.layout = box_layout\n", + "col2 = widgets.VBox([widgets.Label('Scanning'), mag, matrix])\n", + "col2.layout = box_layout\n", + "col3 = widgets.VBox([widgets.Label('Camera length'), cl, restriction])\n", + "col3.layout = box_layout\n", + "col4 = widgets.VBox([widgets.Label('Detection'), camera, binning, dwell_time])\n", + "col4.layout = box_layout\n", + "\n", + "descriptions_beam = widgets.VBox([widgets.Label('Characteristics beam'), beam_res, aperture_res, aperture_res2])\n", + "descriptions_beam.layout = widgets.Layout(border='solid 1px gray', margin='0px 10px 10px 0px', padding='5px 5px 5px 5px')\n", + "descriptions_scan = widgets.VBox([widgets.Label('Characteristics scanning'), fov_res])\n", + "descriptions_scan.layout = widgets.Layout(border='solid 1px gray', margin='0px 10px 10px 0px', padding='5px 5px 5px 5px')\n", + "descriptions_cl = widgets.VBox([widgets.Label('Characteristics camera length'), cl_det_res])\n", + "descriptions_cl.layout = widgets.Layout(border='solid 1px gray', margin='0px 10px 10px 0px', padding='5px 5px 5px 5px')\n", + "descriptions_det = widgets.VBox([widgets.Label('Characteristics detector'), camera_res, pixel_size_res,dwell_time_res])\n", + "descriptions_det.layout = widgets.Layout(border='solid 1px gray', margin='0px 10px 10px 0px', padding='5px 5px 5px 5px')\n", + "\n", + "small_control = widgets.VBox([widgets.Label('Graph controls'),method, ssb_freq_length, scans, descriptions_beam, descriptions_scan, descriptions_cl, descriptions_det])\n", + "controls = widgets.HBox([widgets.VBox([widgets.Label(''), col1, col2, col3, col4]), small_control])\n", + "\n", + "\n", + "\n", + "def ptycho_interact(beam, aperture, probe, cl, matrix, defocus, mag, camera, binning, dwell_time, restriction, method, ssb_freq_length, scans):\n", + " \n", + " ### SINGLE SETTING PARAMETERS ###\n", + " wavelength = pty.get_wavelength(beam)*1e12\n", + " fov = pty.get_fov(mag)\n", + " semi_angle = pty.get_angle(aperture)\n", + " semi_angle_corr = pty.get_angle_corr(aperture) \n", + " current = pty.get_current(probe,aperture) \n", + " pixel_angle = pty.get_pixel_angle(cl, camera, binning)\n", + " cl_det = pty.get_cl_detector(cl)\n", + " frame_rate = pty.get_aq_frec(dwell_time)\n", + " step_size = pty.get_step_size(matrix)\n", + " step_size_corr = np.array(pty.get_step_correction())*pty.get_step_size(matrix)\n", + " beam_diameter = pty.get_beam_diameter(aperture, defocus)\n", + " num_pixels, size_pixel = pty.get_detector(camera, binning)\n", + " beam_diameter_pix = 2*semi_angle_corr/pixel_angle\n", + " detector_cover = num_pixels/2*pixel_angle\n", + " covered_alfas = detector_cover/semi_angle_corr\n", + " overlap = (beam_diameter-step_size_corr)/beam_diameter*100\n", + " if overlap < 0:\n", + " overlap = 0\n", + " dose = ((matrix**2)*(dwell_time/1e6)*(current/1e12/cons.e))/((((matrix-1)*step_size_corr)**2)*100)\n", + " probe_window = (wavelength*cl_det/100)/(2*size_pixel/1e6)/1000 # now in nm\n", + " \n", + " ### ALL CAMERA LENGTHS PARAMETERS ###\n", + " pixel_covers = pty.get_pixel_covers(camera, binning) \n", + " pump_apertures = pty.get_pumping_apertures()\n", + " detector_cover_all = pixel_covers*num_pixels/2\n", + " if restriction == True:\n", + " for x in range(len(detector_cover_all)):\n", + " if detector_cover_all[x] > pump_apertures[x]:\n", + " detector_cover_all[x] = pump_apertures[x] \n", + " detector_pixels_all = np.round(detector_cover_all/pixel_covers) \n", + " ptycho_pixel_size_all = (wavelength * np.array(opt.cameralengths_eff() )) /(detector_pixels_all*size_pixel/1e6)/1e4 \n", + " else:\n", + " ptycho_pixel_size_all = (wavelength * np.array(opt.cameralengths_eff() )) /(num_pixels/2*size_pixel/1e6)/1e4\n", + " \n", + " probe_window_all = (wavelength*np.array(opt.cameralengths_eff())/100)/(2*size_pixel/1e6)/1000 # now in nm\n", + " usable_probe_semi_window_all = probe_window_all/4\n", + " detector_cover_a_all = detector_cover_all/semi_angle_corr\n", + " max_defocus_all = (usable_probe_semi_window_all - pty.get_0beam_diameter(aperture)/2)/np.tan(semi_angle_corr/1000)\n", + " max_defocus_all[max_defocus_all < 0] = 'nan'\n", + " \n", + " omega = np.linspace(1e-6,2,100) \n", + " pctf = pty.get_ssb_ctf()\n", + " apertury = opt.apertures()\n", + " \n", + " ### PROBE ##########################################\n", + " fig = go.Figure() \n", + " fig.update_yaxes(title_text=\" CTF-SSB\")\n", + " if ssb_freq_length == 'mrad': \n", + " for x in apertury:\n", + " fig.add_trace(go.Scatter(showlegend=False, x=pty.get_angle_corr(x) *omega, y=pctf, marker_color='gray', name='SSB-CTF',))\n", + " \n", + " fig.add_trace(go.Scatter(x=semi_angle_corr*omega, y=pctf, marker_color='red', name=str(np.round(semi_angle_corr,2))+ ' mrad',))\n", + " fig.update_xaxes(title_text=\"Spacial frequency (mrad)\", range=[0, 60], zeroline=False)\n", + "\n", + " elif ssb_freq_length == 'A':\n", + " for x in apertury:\n", + " d = 2*wavelength/(np.sin(pty.get_angle_corr(x)*omega/1000))/100\n", + " fig.add_trace(go.Scatter(showlegend=False, x=d, y=pctf, marker_color='gray', name='SSB-CTF',))\n", + " \n", + " fig.add_trace(go.Scatter(x=2*wavelength/(np.sin(semi_angle_corr*omega/1000))/100, y=pctf, marker_color='red', name=str(np.round(semi_angle_corr,2))+ ' mrad',))\n", + " fig.update_xaxes(title_text=\"Spacial frequency (A?)\",range=[0, 2.5], type=\"log\", zeroline=False)\n", + " \n", + " fig.update_layout(legend=dict(orientation=\"h\",yanchor=\"bottom\",y=0.75,xanchor=\"right\",x=1))\n", + " # fig3.update_layout(title={'text': \"CTF\",'y':0.93, 'x':0.12,'xanchor': 'left','yanchor': 'top'})\n", + " fig.update_layout(width=800, height=225, margin = dict(l=110, r=30, t=30, b=30))\n", + "\n", + " \n", + " ### SAMPLE PLANE ##########################################\n", + " yyy = np.append(np.linspace(0,overlap,100), 200)\n", + " wid = np.linspace(0,scans-1,scans) * step_size_corr\n", + " \n", + " fig5 = make_subplots(specs=[[{\"secondary_y\": True}]])\n", + " fig5.update_xaxes(title_text=\"Sample plane (nm)\", range=[0, scans*step_size_corr], zeroline=False)\n", + " fig5.update_yaxes(range=[0, scans*step_size_corr], showticklabels=False)\n", + " \n", + " fig5.add_trace(go.Scatter(showlegend=False, x = scans*step_size_corr*np.ones(len(yyy)), y=yyy, opacity=1,mode='markers',marker_symbol = \"triangle-down\",marker=dict(size=20, color=yyy, colorscale='rainbow_r', showscale=False)),secondary_y=True) \n", + " for x in range(len(wid)):\n", + " fig5.add_trace(go.Scatter(showlegend=False, x=wid, y=np.ones(len(wid))*wid[x], marker_color='blue'),secondary_y=False)\n", + " for x in range(len(wid)): \n", + " for y in range(len(wid)): \n", + " fig5.add_shape(type=\"circle\",xref=\"x\", yref=\"y\", opacity=0.1, fillcolor=\"#FF7F7F\",name=False, x0=wid[x]-beam_diameter/2, y0=wid[y]-beam_diameter/2, x1=wid[x]+beam_diameter/2, y1=wid[y]+beam_diameter/2, line_color=\"red\",secondary_y=False)\n", + "\n", + " fig5.add_trace(go.Scatter(showlegend=True, x=[-1], y=[-1],marker_size=12, marker_color='blue', name='Step '+str(np.round(step_size,3))+' nm ('+str(np.round(step_size_corr,3))+')'))\n", + " fig5.add_trace(go.Scatter(showlegend=True, x=[-1], y=[-1],marker_size=12, marker_color='#FF7F7F', name='Beam ⌀ '+ str(np.round(beam_diameter,2))+\" nm\"))\n", + " fig5.update_layout(legend=dict(orientation=\"v\",yanchor=\"bottom\",y=0.85,xanchor=\"right\",x=0.9))\n", + " fig5.add_trace(go.Scatter(showlegend=False, x = scans*step_size_corr*np.ones(len(yyy)), y=yyy, opacity=1,mode='markers',marker_symbol = \"triangle-down\",marker=dict(size=20, color=yyy, colorscale='rainbow_r', showscale=False)),secondary_y=True) \n", + " fig5.update_yaxes(title_text=\"Overlap %\", range=[0, 100], showticklabels=True, tickvals = [0, 20, 40,50, 60,70, 80, 90, 100], secondary_y=True)\n", + " fig5.update_layout(title={'text': \"Sample\",'y':0.95, 'x':0.10,'xanchor': 'left','yanchor': 'top'})\n", + " fig5.update_layout(width=430, height=380, margin =dict(l=30, r=30, t=10, b=0))\n", + "\n", + " \n", + " ### DETECTOR PLANE ##########################################\n", + " fig6 = go.Figure() \n", + " fig6.update_xaxes(title_text=\"Pixels\", range=[1, num_pixels], zeroline=False)\n", + " fig6.update_yaxes(range=[1, num_pixels], showticklabels=False,)\n", + " for x in np.linspace(1,int(np.round(covered_alfas)),int(np.round(covered_alfas))):\n", + " fig6.add_shape(type=\"circle\",xref=\"x\", yref=\"y\", x0=num_pixels/2-x*beam_diameter_pix, y0=num_pixels/2-x*beam_diameter_pix, x1=num_pixels/2+x*beam_diameter_pix, y1=num_pixels/2+x*beam_diameter_pix,line_color=\"gray\")\n", + " fig6.add_trace(go.Scatter(showlegend=False,x=[num_pixels/2],y=[num_pixels/2-x*beam_diameter_pix],mode=\"lines+text\", text=str(int(2*x))+\"α\",textposition=\"top center\"))\n", + " \n", + " fig6.add_shape(type=\"circle\",xref=\"x\", yref=\"y\", fillcolor=\"#FF7F7F\", x0=num_pixels/2-beam_diameter_pix/2, y0=num_pixels/2-beam_diameter_pix/2, x1=num_pixels/2+beam_diameter_pix/2, y1=num_pixels/2+beam_diameter_pix/2,line_color=\"#FF7F7F\")\n", + " fig6.add_annotation(x=num_pixels/2-beam_diameter_pix/2, y=num_pixels/2, ax=num_pixels/2+beam_diameter_pix/2, ay=num_pixels/2, xref='x',yref='y',axref='x',ayref='y',text='', showarrow=True,arrowhead=3,arrowsize=1,arrowwidth=1,arrowcolor='black')\n", + " fig6.add_annotation(x=num_pixels/2+beam_diameter_pix/2,y=num_pixels/2, ax=num_pixels/2-beam_diameter_pix/2, ay=num_pixels/2, xref='x',yref='y',axref='x',ayref='y', text='', showarrow=True,arrowhead=3,arrowsize=1,arrowwidth=1,arrowcolor='black')\n", + " fig6.add_annotation(x=num_pixels/2, y=num_pixels/2, text=str(np.round(beam_diameter_pix,1))+\" pix\", showarrow=False, yshift=10)\n", + " fig6.add_trace(go.Scatter(x=[1,1.5,1,1.5], y=[1,1,1.5,1.5], name='Pix ∠ ' +str(np.round(pixel_angle,2))+' mrad'))\n", + " fig6.update_layout(legend=dict(orientation=\"v\",yanchor=\"bottom\",y=0.85,xanchor=\"right\",x=1))\n", + " # fig6.update_layout(title={'text': \"Detector\",'y':0.925, 'x':0.10,'xanchor': 'left','yanchor': 'top'})\n", + " fig6.update_layout(xaxis = dict(tickmode = 'array', tickvals = [1, num_pixels/4, num_pixels/2, 3*num_pixels/4, num_pixels])) \n", + " fig6.update_layout(width=350, height=280, margin = dict(l=40, r=60, t=0, b=0))\n", + "\n", + " \n", + " ssb_cl, kolik = pty.cl4ssb(detector_cover_a_all)\n", + " ### MICROSCOPE SCHEME ##########################################\n", + " width = 500 \n", + " det_width = 400\n", + " y_down = np.log(np.max(opt.cameralengths()))\n", + " reduc = 0.4\n", + " \n", + " fig8 = go.Figure() \n", + " fig8.update_xaxes(range=[-width, width], showticklabels=False,zeroline=False)\n", + " fig8.update_yaxes(range=[-y_down, reduc*y_down], showticklabels=False,)\n", + "\n", + " fig8.add_shape(type=\"rect\",xref=\"x\", yref=\"y\",opacity=0.7, fillcolor=\"red\", x0=-100, y0=reduc*0.8*y_down, x1=100, y1=reduc*0.9*y_down,line_color=\"red\")\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[-150, -2*semi_angle, None, 2*semi_angle, 150], y=[reduc*0.8*y_down,reduc*0.8*y_down,None,reduc*0.8*y_down, reduc*0.8*y_down],mode='lines', line=dict(color='black',width=7)))\n", + " fig8.add_annotation(x=-0.7*width, y=reduc*0.8*y_down, text=\"Aperture \"+str(aperture), showarrow=False, yshift=2)\n", + " fig8.add_annotation(x=semi_angle, y=reduc*0.7*y_down, text=\"α \"+str(np.round(semi_angle,2))+\" mrad (\"+str(np.round(semi_angle_corr,2))+\")\", showarrow=True, ax=100, ay=0, yshift=0)\n", + "\n", + " if defocus > 0:\n", + " defo = np.log(defocus)/100\n", + " else:\n", + " defo = 0 \n", + "\n", + " # Beam\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[0, 2*semi_angle, 0, 0], y=[defo*y_down, reduc*0.8*y_down, reduc*0.8*y_down, defo*y_down], mode='lines',fill=\"toself\", fillcolor=\"#FF7F7F\", opacity=0.7,line=dict(color='#FF7F7F',width=1)))\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[0, 0, -2*semi_angle, 0], y=[defo*y_down, reduc*0.8*y_down, reduc*0.8*y_down, defo*y_down],mode='lines', fill=\"toself\", fillcolor=\"red\", opacity=0.7,line=dict(color='red',width=1)))\n", + " fig8.add_annotation(x=0, y=reduc*0.5*y_down,text=\"Current \"+str(np.round(current,1))+\" pA\", showarrow=True,ax=100, ay=0, yshift=0)\n", + " \n", + " if defocus > 0:\n", + " fig8.add_annotation(x=-0.05*width, y=0, ax=-0.05*width, ay=defo*y_down, xref='x',yref='y',axref='x',ayref='y',text='', showarrow=True,arrowhead=1,arrowsize=1,arrowwidth=1,arrowcolor='black')\n", + " fig8.add_annotation(x=-0.05*width, y=defo*y_down, ax=-0.05*width, ay=0, xref='x',yref='y',axref='x',ayref='y',text='', showarrow=True,arrowhead=1,arrowsize=1,arrowwidth=1,arrowcolor='black')\n", + " fig8.add_annotation(x=-0.3*width, y=0.05*y_down,text=\"Δf \"+str(np.round(defocus,1))+\" nm\", showarrow=False)\n", + "\n", + " # Sample\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[-0.8*width, 0.8*width], y=[0, 0], line=dict(color='magenta',width=7), opacity=0.5))\n", + " fig8.add_annotation(x=-0.8*width, y=0,text=\"Sample\", showarrow=False, yshift=15)\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[-np.log(fov)/20*width, np.log(fov)/20*width, np.log(fov)/20*width, -np.log(fov)/20*width, -np.log(fov)/20*width], y=[-0.01*y_down, -0.01*y_down, 0.01*y_down, 0.01*y_down, -0.01*y_down],mode='lines', fill=\"toself\", fillcolor=\"black\", line=dict(color='black',width=1), opacity=0.5))\n", + " fig8.add_annotation(x=0.35*width, y=0.05*y_down,text=\"FoV \"+str(np.round(fov,2))+\" nm\", showarrow=False, ax=50, ay=-30, yshift=0)\n", + " \n", + " # Detector\n", + " y = -np.log(cl)*np.ones(int(num_pixels)+1)\n", + " x = np.linspace(-det_width,det_width, int(num_pixels)+1)\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=x, y=y, mode='markers', marker_symbol=\"line-ns\",marker_line_width=0.5))\n", + " fig8.add_annotation(x=-0.8*width, y=-np.log(cl),text=\"Detector\", showarrow=False,align= 'left', yshift=15)\n", + " \n", + " # Scattering\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[0, det_width, -det_width, 0], y=[defo*y_down, -np.log(cl), -np.log(cl), -np.log(cl), defo*y_down],mode='lines', fill=\"toself\", fillcolor=\"gray\", opacity=0.1,line=dict(color='black',width=1)))\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[0, -det_width, det_width, 0], y=[defo*y_down, -np.log(cl), -np.log(cl), -np.log(cl), defo*y_down],mode='lines', fill=\"toself\", fillcolor=\"gray\", opacity=0.1,line=dict(color='black',width=1)))\n", + " # BF disk\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[0, det_width/covered_alfas, -det_width/covered_alfas, 0], y=[defo*y_down, -np.log(cl), -np.log(cl), -np.log(cl), defo*y_down],mode='lines', fill=\"toself\", fillcolor=\"#FF7F7F\", opacity=0.7,line=dict(color='#FF7F7F',width=1)))\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[0, -det_width/covered_alfas, det_width/covered_alfas, 0], y=[defo*y_down, -np.log(cl), -np.log(cl), -np.log(cl), defo*y_down],mode='lines', fill=\"toself\", fillcolor=\"red\", opacity=0.7,line=dict(color='#FF7F7F',width=1)))\n", + " \n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[-width, width], y=[-np.log(ssb_cl), -np.log(ssb_cl)], opacity=0.8, line=dict(color='green',width=3)))\n", + " fig8.add_annotation(x=-0.7*width, y=-np.log(ssb_cl),text=\"REC for SSB\", showarrow=False,align= 'left', yshift=-15)\n", + " \n", + " # Cover\n", + " fig8.add_annotation(x=0, y=-1.05*np.log(cl), ax=det_width, ay=-1.05*np.log(cl), xref='x',yref='y',axref='x',ayref='y',text='', showarrow=True,arrowhead=3,arrowsize=1,arrowwidth=1,arrowcolor='black')\n", + " fig8.add_annotation(x=det_width,y=-1.05*np.log(cl), ax=0, ay=-1.05*np.log(cl), xref='x',yref='y',axref='x',ayref='y', text='', showarrow=True,arrowhead=3,arrowsize=1,arrowwidth=1,arrowcolor='black')\n", + " fig8.add_annotation(x=det_width/2, y=-1.05*np.log(cl), text=\"Cover \"+str(np.round(detector_cover,1)) + \" mrad;\" + str(np.round(covered_alfas,1))+\"α\", showarrow=False, yshift=-10, xshift=-10)\n", + "\n", + " # CLs\n", + " fig8.add_annotation(x=0.9*width, y=-np.log(1.5),text=\"CL cm\", showarrow=False, yshift=0)\n", + " for x in opt.cameralengths():\n", + " fig8.add_trace(go.Scatter(showlegend=False, x=[-width, width], y=[-np.log(x), -np.log(x)], opacity=0.3, line=dict(color='gray',width=1)))\n", + " fig8.add_annotation(x=0.9*width, y=-np.log(x),text=str(x), showarrow=False, yshift=8)\n", + "\n", + " fig8.update_xaxes(title_text=\"\")\n", + " # fig8.update_layout(title={'text': \"Microscope\" ,'y':1, 'x':0.10,'xanchor': 'left','yanchor': 'top'})\n", + " fig8.update_layout(width=350, height=550, margin = dict(l=10, r=30, t=0, b=0))\n", + " fig8.update_layout(plot_bgcolor='white')\n", + " \n", + " \n", + " ### FINAL CHECKS ########################################## \n", + " align3 = dict(layout=widgets.Layout(width='250px') , style = {'description_width': '150px','button_width': \"100px\"}, disabled=True,) \n", + " \n", + " check1 = widgets.Valid(value= bool(overlap > 60), description='Overlap > 60 %', **align3)\n", + " check2 = widgets.Valid(value= bool(step_size_corr*10 > (wavelength * cl_det)/(num_pixels*size_pixel/1e6)/1e4), description='Ptycho pix < step size', **align3)\n", + " check3 = widgets.Valid(value= bool(beam_diameter/2 < probe_window/2), description='Beam ⌀ < window/2', **align3) \n", + " \n", + " electron_dose = widgets.Label(value = f'Electron dose (e/Å2) '+ str(np.round(dose,1)), **align3) \n", + " checks = widgets.VBox([widgets.Label('Final checks'),check1,check2, check3,electron_dose]) \n", + " checks.layout = widgets.Layout(border='solid 1px gray',margin='10px 10px 10px 10px', padding='10px 10px 10px 10px')\n", + "\n", + " \n", + " ### METHODS ########################################## \n", + " if method == 'direct':\n", + " \n", + " ### CAMERA LENGTH GRAPH ##########################################\n", + " fig4 = make_subplots(specs=[[{\"secondary_y\": True}]])\n", + " fig4.add_trace(go.Scatter(showlegend=False, x=opt.cameralengths(), y=detector_cover_all, marker_color='gray'), secondary_y=False,)\n", + " fig4.add_trace(go.Scatter(showlegend=False, x=opt.cameralengths(), y=detector_cover_a_all,marker_color='gray'), secondary_y=True,)\n", + "\n", + " fig4.add_trace(go.Scatter(x=np.array(cl), y=np.array(covered_alfas), marker_size=12, marker_color='red', name = str(np.round(detector_cover,1))+ \" mrad; \"+str(np.round(covered_alfas,2))+ \" α\"), secondary_y=True,) \n", + " fig4.add_trace(go.Scatter(x=np.array(ssb_cl), y=np.array(kolik),marker_size=12, marker_color='green', name = \"Rec SSB CL\"), secondary_y=True,) \n", + "\n", + " fig4.update_layout(legend=dict(orientation=\"v\",yanchor=\"bottom\",y=0.65,xanchor=\"right\",x=0.9))\n", + " # fig4.update_layout(title={'text': \"CL to α\",'y':0.93, 'x':0.12,'xanchor': 'left','yanchor': 'top'})\n", + " fig4.update_xaxes(title_text=\"Camera length (cm)\", type=\"log\", tickvals = opt.cameralengths())\n", + " fig4.update_layout(xaxis = dict(tickmode = 'array', tickvals = [1, num_pixels/4, num_pixels/2, 3*num_pixels/4, num_pixels]))\n", + " fig4.update_yaxes(title_text=\" Cover angle (mrad)\", secondary_y=False)\n", + " fig4.update_yaxes(title_text=\" Cover angle (α)\", secondary_y=True)\n", + " fig4.update_layout(xaxis = dict(tickmode = 'array', tickvals = opt.cameralengths()))\n", + " fig4.update_layout(width=800, height=225, margin =dict(l=110, r=30, t=30, b=30))\n", + " \n", + " \n", + " ### SHOWING ###\n", + " right_column = widgets.VBox([go.FigureWidget(fig),go.FigureWidget(fig5),go.FigureWidget(fig4)])\n", + " left_column = widgets.VBox([go.FigureWidget(fig8), go.FigureWidget(fig6)])\n", + " display(widgets.HBox([left_column, right_column, checks]))\n", + " \n", + " \n", + " if method == 'iterative':\n", + " \n", + " ### PROBE WINDOW ##########################################\n", + " if beam_diameter < probe_window/2:\n", + " color = \"green\"\n", + " name = 'Beam ⌀ is fine'\n", + " elif beam_diameter < probe_window:\n", + " color = \"orange\"\n", + " name = 'Beam ⌀ is quite big'\n", + " else:\n", + " color = \"red\"\n", + " name = 'Beam ⌀ is too big'\n", + "\n", + " fig7 = go.Figure() \n", + " fig7.add_shape(type=\"rect\",xref=\"x\", yref=\"y\",opacity=0.3, fillcolor=\"LightSkyBlue\", x0=probe_window/4, y0=probe_window/4, x1=3*probe_window/4, y1=3*probe_window/4,line_color=\"LightSkyBlue\")\n", + " fig7.add_trace(go.Scatter(x=np.array(probe_window/2), y=np.array(probe_window/2), showlegend=False, marker_color=\"LightSkyBlue\"))\n", + " fig7.add_trace(go.Scatter(x=np.array(probe_window/2), y=np.array(probe_window/2), name=name, showlegend=True, marker_color=color))\n", + " fig7.add_annotation(x=probe_window/2, y=3*probe_window/4, text=\"Usable area \", showarrow=False, yshift=-15)\n", + " fig7.add_shape(type=\"circle\",xref=\"x\", yref=\"y\", opacity=0.9, fillcolor=color, x0=probe_window/2-beam_diameter/2, y0=probe_window/2-beam_diameter/2, x1=probe_window/2+beam_diameter/2, y1=probe_window/2+beam_diameter/2, line_color=color,)\n", + " fig7.update_xaxes(title_text=\"Probe window (nm)\", range=[0, probe_window], tickvals = [0, np.round(probe_window/4,2), np.round(probe_window/2,2), np.round(3*probe_window/4,2), np.round(probe_window,2)-0.01])\n", + " fig7.update_yaxes(range=[0, probe_window], showticklabels=False,)\n", + " fig7.update_layout(legend=dict(orientation=\"v\",yanchor=\"bottom\",y=0.9,xanchor=\"right\",x=1))\n", + " fig7.update_layout(title={'text': \"Probe window \",'y':0.925, 'x':0.10,'xanchor': 'left','yanchor': 'top'})\n", + " fig7.update_layout(width=400, height=380, margin =dict(l=30, r=30, t=10, b=0)) \n", + " \n", + " ### CAMERA LENGTH TABLE ########################################## \n", + " dictionary = {}\n", + " tab = np.array([np.round(ptycho_pixel_size_all,2),np.round(detector_cover_all,1), np.round(detector_cover_a_all,1), np.round(max_defocus_all,1), np.array(opt.cameralengths())])\n", + " \n", + " align6 = dict(layout=widgets.Layout(width='50px') , style = {'description_width': '0px','button_width': \"33px\"}, disabled=True,)\n", + " align7 = dict(layout=widgets.Layout(width='130px') , style = {'description_width': '0px','button_width': \"110px\"}, disabled=True,)\n", + " for i in range(0,12):\n", + " button_style = 'success'\n", + " data = tab[:,int(i)] # 0. ptycho pix, 1.det cov 2.det cov a, 3.max def, 4.cl\n", + " \n", + " if data[1] > 1000*math.radians(10):\n", + " button_style = 'danger' \n", + " if data[0] > step_size_corr*10:\n", + " button_style = 'warning'\n", + " if data[3] < defocus:\n", + " button_style = 'danger'\n", + " if data[2] < 1:\n", + " button_style = 'warning' \n", + " \n", + " dictionary[i] = widgets.ToggleButtons(options=data, description='',button_style=button_style, **align6)\n", + " \n", + " legend = widgets.ToggleButtons(options=['Ptycho pix. (A)','Det. cover (mrad)','Det. cover (α)', 'Max. defocus (nm)','Nominal CL (cm)'], description='',button_style='', **align7) \n", + " cltab = widgets.HBox([legend,dictionary[0],dictionary[1],dictionary[2],dictionary[3],dictionary[4],dictionary[5],dictionary[6],dictionary[7],dictionary[8],dictionary[9], dictionary[10],dictionary[11]])\n", + " cltab = widgets.VBox([widgets.Label('Chose your reconstructed ptychographic pixe size, check needed defocus and set CL'), cltab])\n", + " cltab.layout = widgets.Layout(border='solid 0px gray',margin='10px 10px 10px 10px', padding='10px 10px 10px 10px')\n", + " \n", + " \n", + " \n", + " ### SHOWING ###\n", + " sample_overlap = widgets.HBox([go.FigureWidget(fig5), go.FigureWidget(fig7)]) \n", + " right_column = widgets.VBox([go.FigureWidget(fig), sample_overlap, cltab ]) \n", + " left_column = widgets.VBox([go.FigureWidget(fig8), go.FigureWidget(fig6)]) \n", + " \n", + " display(widgets.HBox([left_column, right_column, checks]))\n", + " \n", + " return \n", + " \n", + "gui = interactive_output(ptycho_interact, {\"beam\": beam, \"aperture\": aperture, \"probe\": probe, \"cl\": cl, \"matrix\": matrix, \"defocus\": defocus, \"mag\": mag, \"camera\": camera,\n", + " \"binning\": binning, \"dwell_time\": dwell_time, \"restriction\": restriction, \"method\": method, \"ssb_freq_length\": ssb_freq_length, \"scans\": scans}) " + ] + }, + { + "cell_type": "markdown", + "id": "1e474cb7-5fd4-4113-a3fa-ddf5ca152ce5", + "metadata": {}, + "source": [ + "![title](./ptychoscopy_logo.png)\n", + "\n", + "Jupyter based interactive data acquisition tool designed for appropriate ptychographic data collection. It computes nessesary characteristics which play crutial role in final data reconstruction. \n", + "You can chose of **Direct methods** (mainly Single Side Band ptychography) or **Iterative reconstruction** which takes probe defocus into account. With this tool, you can check for probe CTF, scanning step size, probe overlap, detector camera length a proper angular range collection, reconstructed probe size and many more.\n", + "### Calibrations\n", + "Every STEM has its own combination of parameters (magnifications, probe currents, aperture angles, camera lengths or detectors). These have to be taken into account for proper data acquisition design. Before first use, fill your own values into the file **calibrations.xlsx**. Instructions how to fill are in the sheet called HELP.\n", + "\n", + "### Controls\n", + "List of controls consists of all parameters needed for proper dataset acquisition design:\n", + "\n", + "| Beam | | Scanning | | Camera length | | Detection | |\n", + "|------ | ------| ------| ------| ------| ------| ------| ------|\n", + "| **Aperture**| probe forming aperture gives probe semi-angle (α) | **Magnification**| controls image field of view | **Nominal CL** | sample to detector distance gives maximal detected angle as well as angular sampling | **Detector** | detector used for data collection | \n", + "| **Energy** | energy of primary electron beam in keV ... gives final pixel size | **Matrix** | gives beam position to position distance + beam defocus = beam overlap | **Use PAAR** | Pumping Aperture Anglar Restriction, build-in aperture may limit maximal scattering angles for shortest camera lengths |**Binning** | reduction in pixel array size usually increas maximum frame rate |\n", + "| **Probe** | probe setting together with chosen probe forming aperture give probe current| | | | | **Dwell time**|single position beam time ... check your detector characteristics |\n", + "| **Defocus** | moves focus out of the sample plane - enlarges beam diameter | | | | | | |\n", + "\n", + "### Start the interactive gui by running the field\n", + "```python\n", + "display(widgets.HBox([controls, gui]))\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "7937f054-fcd0-4e67-a20f-7696f5903a94", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b09b238a260d4425ab717013806e2bce", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HBox(children=(VBox(children=(Label(value=''), VBox(children=(Label(value='Beam'), Dropdown(des…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(widgets.HBox([controls, gui]))" + ] + }, + { + "cell_type": "markdown", + "id": "e46226d7-fc21-4c83-8363-225ecd92f28e", + "metadata": {}, + "source": [ + "#### Instructions for **SSB**\n", + " \n", + "- set beam energy\n", + "- set probe current\n", + "- set detector (consider binning and minimal dwell time)\n", + "- set defocus to 0 \n", + "- set probe angle -> get needed CTF\n", + "- set magnification -> get FoV\n", + "- set scanning matrix -> get sampling\n", + "- set CL -> get detector cover (just over 1 alfa) \n", + "- check final dose\n", + "\n", + "#### Instructions for **Iterative reconstructions**\n", + "\n", + "- set beam energy\n", + "- set probe current\n", + "- set detector (consider binning and minimal dwell time)\n", + "- set probe angle -> get reconstructed pixel size\n", + "- set magnification -> get FoV\n", + "- set scanning matrix, defocus -> get sampling and overlap\n", + "- set CL to get final pixel size (table)\n", + "- check warnings in orange and danger in red\n", + "- check final dose\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "position": { + "height": "144.294px", + "left": "1149.02px", + "right": "19.9884px", + "top": "119.988px", + "width": "349.977px" + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ptychoscopy_logo.png b/ptychoscopy_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e817f3beb0468d06673344975ab0e417b6ee61 GIT binary patch literal 16036 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 literal 0 HcmV?d00001