Files
ptychoscopy/ptychoScopy.ipynb
2023-12-07 15:54:02 +01:00

940 lines
59 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"execution_count": 462,
"id": "7425242d-3c91-4c1e-a424-08625a38ee7a",
"metadata": {},
"outputs": [],
"source": [
"### Initial packages import ###################################################\n",
"import numpy as np\n",
"import scipy.constants as cons\n",
"import plotly.graph_objects as go\n",
"from scipy.interpolate import splrep, BSpline \n",
"from copy import deepcopy\n",
"from pandas import read_excel\n",
"from IPython.display import display\n",
"from plotly.subplots import make_subplots\n",
"from ipywidgets import interactive_output, HBox, VBox, Layout, Label, Valid, ToggleButtons, RadioButtons, Dropdown, IntSlider, Checkbox\n",
"\n",
"# print(\"Numpy version \", np.__version__)\n",
"# print(\"Pandas version \", pd.__version__)\n",
"# print(\"Ipywidgets version \", widgets.__version__)\n",
"# print(\"Plotly version \", plotly.__version__)\n",
"\n",
"### Function definitions ###################################################\n",
"\n",
"calib = r'calibrations.xlsx' # 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 = 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 = 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(read_excel(self.calib,sheet_name='Probes').Probe)\n",
" probes = probes[3::]\n",
" return probes\n",
" \n",
" def magnifications(self):\n",
" excel_data = read_excel(self.calib,sheet_name='Ranges')\n",
" magnifications = list(excel_data.Magnification)\n",
" return magnifications\n",
" \n",
" def energies(self): \n",
" excel_data = read_excel(self.calib,sheet_name='Ranges')\n",
" energies = [str(e) for e in list(excel_data.BeamEnergy)]\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 = 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(read_excel(self.calib,sheet_name='Pixel').NominalCL)\n",
" return cameralengths\n",
" \n",
" def cameralengths_eff(self): \n",
" cameralengths_eff = list(read_excel(self.calib,sheet_name='Pixel').DetectorCL)\n",
" return cameralengths_eff\n",
"\n",
" def dwelltimes(self): \n",
" excel_data = read_excel(self.calib,sheet_name='Ranges')\n",
" dwelltimes = [str(e) for e in list(excel_data.DwellTime)]\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 = read_excel(calib, sheet_name='Probes',header=0)\n",
" excel_line = excel_data[excel_data.Probe.isin(['SemiAngle'])]\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 = read_excel(calib, sheet_name='Probes',header=0)\n",
" excel_line = excel_data[excel_data.Probe.isin(['SemiAngleCorr'])]\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 = 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 = 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 = 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 = 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 = read_excel(calib,sheet_name='Pixel')\n",
" excel_line = excel_data[excel_data.NominalCL.isin([cl])]\n",
" self.cl_detector = excel_line['DetectorCL']\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(read_excel(calib,sheet_name='Ranges').StepSizeCorr)]\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 = read_excel(calib, sheet_name='Probes')\n",
" excel_line = excel_data[excel_data.Probe.isin(['Def0Diameter'])]\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 = read_excel(calib, sheet_name='Probes')\n",
" excel_line = excel_data[excel_data.Probe.isin(['Def0Diameter'])]\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 = read_excel(calib, sheet_name='Detector')\n",
" excel_line = excel_data[excel_data.Type.isin(['Array'])]\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(['RealSize'])]\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 = read_excel(calib, sheet_name='Detector')\n",
" excel_line = excel_data[excel_data.Type.isin(['Array'])]\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 = read_excel(calib, sheet_name='Detector')\n",
" excel_line = excel_data[excel_data.Type.isin(['RealSize'])]\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 = 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 = read_excel(calib,sheet_name='Pixel')\n",
" pump_apert = [str(e) for e in list(excel_data.PAAR)]\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 = 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 computing of various parameters.\"\"\"\n",
" \n",
" def __init__(self):\n",
" self.calib = calib\n",
" \n",
" \n",
" def show_wavelength(self,caller):\n",
" beam_res.value = f'λ (pm) {str(\"{:.1f}\".format(pty.get_wavelength(caller.new)*1e12))}'\n",
" return beam_res.value\n",
" \n",
" def show_probe_angle(self,caller):\n",
" aperture_res.value = f'Probe semi-angle (mrad) {\"{:.1f}\".format(pty.get_angle(caller.new))}' \n",
" return aperture_res.value\n",
" \n",
" def show_corrprobe_angle(self,caller):\n",
" aperture_res2.value = f'corr. {\"{:.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'FoV (nm) {\"{:.1f}\".format(pty.get_fov(caller.new))}'\n",
" return fov_res.value\n",
" \n",
" def show_frame_rate(self,caller):\n",
" dwell_time_res.value = f'Rate (kHz) {\"{:.1f}\".format(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 CL (cm) {\"{:.1f}\".format(pty.get_cl_detector(caller.new))}' \n",
" return cl_det_res.value\n",
" \n",
" \n",
"inte = interaction() \n"
]
},
{
"cell_type": "code",
"execution_count": 761,
"id": "8055b802-cf83-4250-aea9-54e3e6b73db0",
"metadata": {},
"outputs": [],
"source": [
"### CONTROLS ##########################################\n",
"\n",
"ali = dict(style = {'description_width': '60px','button_width': '33px'}, button_style='',disabled=False)\n",
"ali2 = dict(style = {'description_width': '60px','button_width': '90px'}, button_style='',disabled=False)\n",
"header = Button(description='STEM control panel',layout=Layout(width='auto', grid_area='header'), style=ButtonStyle(button_color='silver'))\n",
"\n",
"beam_set = Button(description='Electron beam settings',layout=Layout(width='auto', grid_area='beam_set'), style=ButtonStyle(button_color='lightsteelblue')) \n",
"beam = ToggleButtons(options=opt.energies(), value=200,description='E (keV)', layout=Layout(width='auto', grid_area='beam_set1'), **ali)\n",
"aperture = ToggleButtons(options=opt.apertures(), value='30um', description='Aperture', layout=Layout(width='auto', grid_area='beam_set2'), **ali)\n",
"probe = ToggleButtons(options=opt.probes(), value='8C', description='Probe', layout=Layout(width='auto', grid_area='beam_set3'), **ali)\n",
"defocus = IntSlider(description='Δf (nm)', value=0, min=0, max=1000, step=1, continuous_update=False, layout=Layout(width='auto', grid_area='beam_set4'), **ali)\n",
"\n",
"scanning_set = Button(description='Scanning parameters',layout=Layout(width='auto', grid_area='scanning_set'),style=ButtonStyle(button_color='lightsteelblue'))\n",
"mag = ToggleButtons(options=opt.magnifications(),value=3,description='Mag. Mx', layout=Layout(width='auto', grid_area='scanning_set1'), **ali)\n",
"matrix = ToggleButtons(options=opt.mappings(), value=2048, description='Matrix', layout=Layout(width='auto', grid_area='scanning_set2'), **ali)\n",
"dwell_time = ToggleButtons(options=opt.dwelltimes(), value=10, description='t (μs)', layout=Layout(width='auto', grid_area='scanning_set3'), **ali)\n",
"\n",
"\n",
"\n",
"camera_set = Button(description='Detection',layout=Layout(width='auto', grid_area='camera_set'),style=ButtonStyle(button_color='lightsteelblue'))\n",
"cl = ToggleButtons(options=opt.cameralengths(), value=12, description='CL (cm)', layout=Layout(width='auto', grid_area='cl_set1'), **ali)\n",
"camera = ToggleButtons(options=opt.detectors(), description='Detector', layout=Layout(width='auto', grid_area='camera_set1'), **ali2)\n",
"restriction = ToggleButtons(options=[('.',False), ('..',True)], value = False, description='PAAR',icons = ['times','check'], layout=Layout(width='auto', grid_area='camera_set2'), **ali)\n",
"binning = ToggleButtons(options=[('.',1), ('2×2',2), ('4×4',4), ('6×6',6), ('8×8',8)], value=1, description='Binning', icons = ['times','',''], layout=Layout(width='auto', grid_area='camera_set3'), **ali)\n",
"\n",
"beam_res = Label(value = f'λ (pm) '+ str(\"{:.1f}\".format(pty.get_wavelength(beam.value)*1e12)),layout=Layout(width='auto', grid_area='sidebar1'),)\n",
"beam.observe(inte.show_wavelength, names='value') \n",
"\n",
"aperture_res = Label(value = f'Probe semi-angle (mrad) '+ str(\"{:.1f}\".format(pty.get_angle(aperture.value))), layout=Layout(width='auto', grid_area='sidebar2'),)\n",
"aperture.observe(inte.show_probe_angle, names='value') \n",
"\n",
"aperture_res2 = Label(value = f'corr. '+ str(\"{:.2f}\".format(pty.get_angle_corr(aperture.value))),layout=Layout(width='auto', grid_area='sidebar5'),)\n",
"aperture.observe(inte.show_corrprobe_angle, names='value') \n",
"\n",
"fov_res = Label(value = f'FoV (nm) '+ str(\"{:.1f}\".format(pty.get_fov(mag.value))),layout=Layout(width='auto', grid_area='sidebar3'),) \n",
"mag.observe(inte.show_fov, names='value') \n",
"\n",
"dwell_time_res = Label(value = f'Rate (kHz) '+ str(\"{:.1f}\".format(pty.get_aq_frec(dwell_time.value))),layout=Layout(width='auto', grid_area='sidebar4'),) \n",
"dwell_time.observe(inte.show_frame_rate, names='value') \n",
"\n",
"footer = Button(description='Footer',layout=Layout(width='auto', grid_area='footer'),style=ButtonStyle(button_color='silver'))\n",
"\n",
"\n",
"controls = GridBox(children=[header, beam_set, beam, aperture, probe, defocus, scanning_set, mag,matrix,dwell_time, cl,camera_set, camera, restriction, binning, beam_res, aperture_res,aperture_res2,fov_res, dwell_time_res,footer],\n",
" layout=Layout(\n",
" width='22%',\n",
" grid_template_rows='auto auto auto',\n",
" grid_template_columns='30% 30% 20% 20%',\n",
" grid_template_areas='''\n",
" \"header header header header\"\n",
" \"beam_set beam_set beam_set beam_set \" \n",
" \"beam_set1 beam_set1 beam_set1 beam_set1 \"\n",
" \"beam_set2 beam_set2 beam_set2 beam_set2 \"\n",
" \". sidebar2 sidebar2 sidebar5 \"\n",
" \"beam_set3 beam_set3 beam_set3 beam_set3 \"\n",
" \"beam_set4 beam_set4 beam_set4 beam_set4 \"\n",
" \"scanning_set scanning_set scanning_set scanning_set \" \n",
" \"scanning_set1 scanning_set1 scanning_set1 scanning_set1 \"\n",
" \"scanning_set2 scanning_set2 scanning_set2 scanning_set2 \"\n",
" \"scanning_set3 scanning_set3 scanning_set3 scanning_set3 \"\n",
" \"camera_set camera_set camera_set camera_set \"\n",
" \"cl_set1 cl_set1 cl_set1 cl_set1 \"\n",
" \"camera_set1 camera_set1 camera_set1 camera_set1 \"\n",
" \"camera_set3 camera_set3 camera_set3 camera_set3 \"\n",
" \"camera_set2 camera_set2 camera_set2 camera_set2 \"\n",
" ''')\n",
" )\n",
"\n",
"# SMALL CONTROLS\n",
"\n",
"\n",
"header = Button(description='Graph controls',layout=Layout(width='auto', grid_area='header'),style=ButtonStyle(button_color='whitesmoke'))\n",
"\n",
"method = ToggleButtons(options=[('SSB','direct'), ('Iter','iterative')], description='Method', tooltips=['SSB', 'PIE, MLc, DM'],\n",
" layout=Layout(width='auto', grid_area='methods'), style = {'description_width': descr,'button_width': butt}, button_style='',disabled=False,)\n",
"\n",
"graph_size = ToggleButtons(options=[('S', 90), ('M', 100), ('L', 120)], value=100, description='Graphs size',\n",
" layout=Layout(width='auto', grid_area='gr_size'), style = {'description_width': descr,'button_width': butt}, button_style='',disabled=False,)\n",
"\n",
"ctf_xaxis = RadioButtons(options=['α','mrad', 'A'], description='CTF x-axis:',\n",
" layout=Layout(width='auto', grid_area='ctxaxis'), style = {'description_width': descr,'button_width': butt}, disabled=False,)\n",
"\n",
"cl_check_laa = Checkbox(value=True, description='Low anlge approximation', indent=False,\n",
" layout=Layout(width='auto', grid_area='check1'), style = {'description_width': descr,'button_width': butt}, disabled=False,) # Low angle approximation check - scattering angle < 10 deg\n",
"cl_check_pix = Checkbox(value=True, description='Recon. pixel < step size',indent=False,\n",
" layout=Layout(width='auto', grid_area='check2'), style = {'description_width': descr,'button_width': butt}, disabled=False,) # Reconstructed pixel size < scanning step size \n",
"cl_check_def = Checkbox(value=True, description='Beam fits in probe window', indent=False,\n",
" layout=Layout(width='auto', grid_area='check3'), style = {'description_width': descr,'button_width': butt}, disabled=False,) # Is probe window big enough to accomodate defocused probe?\n",
"cl_check_1bf = Checkbox(value=True, description='Detector cover < 1α', indent=False,\n",
" layout=Layout(width='auto', grid_area='check4'), style = {'description_width': descr,'button_width': butt}, disabled=False,) # Detector cover is lower than 1 α\n",
"cl_check_nbf = Checkbox(value=True, description='Detector cover > 6α', indent=False,\n",
" layout=Layout(width='auto', grid_area='check5'), style = {'description_width': descr,'button_width': butt}, disabled=False,) # Detector cover is higher than 6 α\n",
"\n",
"footer = Button(description='Interactive results',layout=Layout(width='auto', grid_area='footer'),style=ButtonStyle(button_color='whitesmoke'))\n",
"\n",
"small_controls = GridBox(children=[header, method, graph_size, ctf_xaxis, cl_check_laa,cl_check_pix,cl_check_def, cl_check_1bf, cl_check_nbf, footer],\n",
" layout=Layout(\n",
" width='100%',\n",
" grid_template_rows='auto auto auto',\n",
" grid_template_columns='40% 20% 20% 20%',\n",
" grid_template_areas='''\n",
" \"header header header header \"\n",
" \"methods ctxaxis check1 check2\"\n",
" \"gr_size ctxaxis check3 check5\"\n",
" \". ctxaxis check4 . \" \n",
" \"footer footer footer footer\"\n",
" ''')\n",
" )\n",
"\n",
"\n",
"def ptycho_interact(beam, aperture, aperture_res, aperture_res2, probe, cl, matrix, defocus, mag, camera, binning, dwell_time, restriction, method, ctf_xaxis, cl_check_laa, cl_check_pix, cl_check_def, cl_check_1bf, cl_check_nbf, graph_size):\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",
" \n",
" ### SAMPLE PLANE ##########################################\n",
" points = 5\n",
" yyy = np.append(np.linspace(0,overlap,100), 200)\n",
" wid = np.linspace(0,points-1,points) * step_size_corr\n",
" \n",
" fig5 = make_subplots(specs=[[{\"secondary_y\": True}]])\n",
" fig5.update_xaxes(title_text=\"Sample plane (nm)\", range=[0, points*step_size_corr], showgrid=False, zeroline=False)\n",
" fig5.update_yaxes(range=[0, points*step_size_corr],showgrid=False, showticklabels=False)\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(10*step_size,3))+' Å ('+str(np.round(10*step_size_corr,3))+')'))\n",
" if method == 'direct':\n",
" xx = 2*wavelength/(np.sin(semi_angle_corr*omega/1000))/100\n",
" fig5.add_trace(go.Scatter(showlegend=True, x=[-1], y=[-1],marker_size=12, marker_color='green', name='Rec step '+str(np.round(xx[-1]/3,2))+' Å')) \n",
" \n",
" fig5.add_trace(go.Scatter(showlegend=True, x=[-1], y=[-1],marker_size=12, marker_color='#FF7F7F', name='Beam ⌀ '+ str(np.round(10*beam_diameter,2))+\" Å\"))\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 = points*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],showgrid=False, 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.30,'xanchor': 'left','yanchor': 'top'})\n",
" fig5.update_layout(width=4.5*graph_size, height=3.0*graph_size, margin =dict(l=1.3*graph_size, r=0.3*graph_size, t=0.1*graph_size, b=0))\n",
"\n",
" \n",
" \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.add_trace(go.Scatter(x=[1,1.5,1,1.5], y=[1,1,1.5,1.5], name='Pixel size ' +str(np.round(size_pixel,2))+' um'))\n",
" fig6.update_layout(legend=dict(orientation=\"v\",yanchor=\"bottom\",y=0.80,xanchor=\"right\",x=1))\n",
" fig6.update_layout(title={'text': \"Detector\",'y':0.95, 'x':0.11,'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=3.5*graph_size, height=3*graph_size, margin = dict(l=0.35*graph_size, r=0.65*graph_size, t=0.3*graph_size, b=0))\n",
"\n",
" \n",
" \n",
" \n",
" focusdepth = wavelength/(semi_angle_corr/1000)**2/1000\n",
" ssb_cl, kolik = pty.cl4ssb(detector_cover_a_all)\n",
" ### MICROSCOPE SCHEME ##########################################\n",
" width = 500 # in-graph setting\n",
" det_width = 400 # in-graph setting\n",
" y_down = np.log(np.max(opt.cameralengths()))\n",
" reduc = 0.4 # in-graph setting\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=\"<b>Aperture </b>\", showarrow=False, yshift=2)\n",
" fig8.add_annotation(x=-0.7*width, y=reduc*0.8*y_down, text=str(aperture), showarrow=False, yshift=-15)\n",
" fig8.add_annotation(x=0.0*width, y=-y_down,text='Acquisition time (s) '+ str(np.round(matrix*matrix*dwell_time/1e6,1)), showarrow=False, yshift=-10)\n",
" fig8.add_annotation(x=0, y=reduc*0.96*y_down,text='λ (pm) '+ str(np.round((pty.get_wavelength(beam)*1e12),2)), showarrow=False, yshift=0)\n",
" \n",
" \n",
" \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=70, 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.7*width, y=0,text=\"<b>Sample</b>\", 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.4*width, y=0.05*y_down,text=\"FoV \"+str(np.round(fov,2))+\" nm\", showarrow=False, yshift=0)\n",
" fig8.add_annotation(x=0.5*width, y=0.05*y_down,text=\"Dose \"+str(np.round(dose,1))+\" e/Å2\", showarrow=False, yshift=-40)\n",
" fig8.add_annotation(x=-0.5*width, y=0.05*y_down,text=\"DoF \"+str(np.round(focusdepth,1))+\" nm\", showarrow=False, yshift=-40)\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.7*width, y=-np.log(cl),text=\"<b>Detector</b>\", showarrow=False,align= 'left', yshift=15)\n",
" fig8.add_annotation(x=-0.7*width, y=-np.log(cl),text=str(np.round(cl_det,2))+' cm', showarrow=False,align= 'left', yshift=-15) \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*width, y=-np.log(ssb_cl),text=\"<b></b>\", showarrow=False,align= 'left', yshift=15)\n",
" \n",
" if restriction == True:\n",
" fig8.add_trace(go.Scatter(showlegend=False, x=[-250, -4*semi_angle, None, 4*semi_angle, 250], y=[-reduc*0.2*y_down,-reduc*0.2*y_down,None,-reduc*0.2*y_down, -reduc*0.2*y_down],mode='lines', line=dict(color='black',width=7)))\n",
" fig8.add_annotation(x=-0.35*width, y=-reduc*0.2*y_down,text=\"<b>PAAR</b>\", 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.85*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=3.0*graph_size, height=7.2*graph_size, margin = dict(l=0, r=0, t=0.2*graph_size, b=0))\n",
" fig8.update_layout(plot_bgcolor='white')\n",
" \n",
" ### FINAL CHECKS ########################################## \n",
" align_check = dict(layout=Layout(width='200px') , style = {'description_width': '150px','button_width': \"50px\"}, disabled=True,) \n",
" check1 = Valid(value= bool(overlap > 60), description='Overlap > 60 %', **align_check)\n",
" check2 = Valid(value= bool(step_size_corr*10 > (wavelength * cl_det)/(num_pixels*size_pixel/1e6)/1e4), description='Ptycho pix < step size', **align_check)\n",
" check3 = Valid(value= bool(beam_diameter/2 < probe_window/2), description='Beam ⌀ < window/2', **align_check) \n",
" \n",
"\n",
"\n",
" \n",
" ### METHODS ########################################## \n",
" if method == 'direct':\n",
" \n",
" \n",
" ### PROBE ##########################################\n",
" fig = go.Figure() \n",
" fig.update_yaxes(title_text=\"Contrast transfer func.\")\n",
" xx = 2*wavelength/(np.sin(semi_angle_corr*omega/1000))/100\n",
" \n",
" if ctf_xaxis == 'α': \n",
" fig.add_trace(go.Scatter(x=omega, y=pctf, marker_color='red', name=str(np.round(semi_angle_corr,2))+ ' mrad',))\n",
" fig.update_xaxes(title_text=\"Scattering angle (α)\", range=[0, 2], zeroline=False)\n",
" \n",
" if ctf_xaxis == '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 ctf_xaxis == '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=xx, y=pctf, marker_color='red', name=str(np.round(semi_angle_corr,2))+ ' mrad',))\n",
" fig.update_xaxes(title_text=\"Real space distance (Å)\", range=[-0.2, 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=7.65*graph_size, height=2.25*graph_size, margin = dict(l=1.3*graph_size, r=0.3*graph_size, t=0.3*graph_size, b=0.3*graph_size))\n",
" \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",
" 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 = \"Recommended CL\"), secondary_y=True,) \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=\"Nominal 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)\", range=[0, 1.1*np.max(detector_cover_all)], secondary_y=False)\n",
" fig4.update_yaxes(title_text=\" Cover angle (α)\", range=[0, 1.1*np.max(detector_cover_a_all)], showgrid=False, secondary_y=True)\n",
" fig4.update_layout(xaxis = dict(tickmode = 'array', tickvals = opt.cameralengths()))\n",
" fig4.update_layout(width=8.0*graph_size, height=2.25*graph_size, margin =dict(l=1.3*graph_size, r=0.25*graph_size, t=0.3*graph_size, b=0.3*graph_size))\n",
"\n",
" ### SHOWING ###\n",
" right_column = VBox([go.FigureWidget(fig),HBox([go.FigureWidget(fig5),go.FigureWidget(fig6)]),go.FigureWidget(fig4)])\n",
" left_column = VBox([go.FigureWidget(fig8)])\n",
" \n",
"\n",
" \n",
" \n",
" total = HBox([left_column, right_column])\n",
" total.layout = Layout(border='solid 0px gray',margin='10px 10px 10px 40px', padding='10px 10px 10px 10px')\n",
" \n",
" display(total)\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" if method == 'iterative': \n",
" \n",
" ### PROBE ##########################################\n",
" fig = go.Figure() \n",
" fig.update_yaxes(title_text=\"Contrast transfer func.\")\n",
" \n",
" \n",
" omega =np.array([0, 0.07, 0.125 ,0.25, 0.50, 0.75, 1.00, 1.25, 1.50, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.00, 4.25, 4.500, 4.750, 5.000])\n",
" pctf = np.array([0, 0.8, 0.95 ,0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.80, 0.6, 0.40, 0.3, 0.20, 0.1, 0.07, 0.05, 0.03, 0.02, 0.01, 0.005, 0.002, 0.001])\n",
" \n",
" tck = splrep(omega, pctf, s=0.01)\n",
" xnew = np.linspace(1e-6,5,100)\n",
" pctf_new = BSpline(*tck)(xnew)\n",
" \n",
" if ctf_xaxis == 'α': \n",
" fig.add_trace(go.Scatter(showlegend=False, x=xnew, y=pctf_new, marker_color='red',))\n",
" fig.update_xaxes(title_text=\"Scattering angle (α)\", range=[0, 5], zeroline=False)\n",
" pctf_upper = pctf_new+xnew/30\n",
" pctf_lower = pctf_new-xnew/30\n",
" fig.add_trace(go.Scatter(showlegend=False, x=np.concatenate((xnew,xnew[::-1])), y=np.concatenate((pctf_lower,pctf_upper[::-1])),fill='toself', fillcolor='rgba(0,100,80,0.2)', line=dict(color='rgba(255,255,255,0)'),hoverinfo=\"skip\",))\n",
" fig.update_yaxes(range=[0, 1.1])\n",
" \n",
" if ctf_xaxis == 'mrad': \n",
" for x in apertury:\n",
" fig.add_trace(go.Scatter(showlegend=False, x=pty.get_angle_corr(x) *xnew, y=pctf_new, marker_color='gray', name='',))\n",
"\n",
" fig.add_trace(go.Scatter(x=semi_angle_corr*xnew, y=pctf_new, marker_color='red', name=str(np.round(semi_angle_corr,2))+ ' mrad',))\n",
" fig.update_xaxes(title_text=\"Spacial frequency (mrad)\", range=[0, 100], zeroline=False)\n",
"\n",
" elif ctf_xaxis == 'A':\n",
" for x in apertury:\n",
" d = 2*wavelength/(np.sin(pty.get_angle_corr(x)*xnew/1000))/100\n",
" fig.add_trace(go.Scatter(showlegend=False, x=d, y=pctf_new, marker_color='gray', name='CTF',))\n",
" \n",
" xx = 2*wavelength/(np.sin(semi_angle_corr*xnew/1000))/100\n",
" fig.add_trace(go.Scatter(x=xx, y=pctf_new, marker_color='red', name=str(np.round(semi_angle_corr,2))+ ' mrad',))\n",
" fig.update_xaxes(title_text=\"Real space distance (Å)\",range=[-0.5, 2], 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=7.65*graph_size, height=2.25*graph_size, margin = dict(l=1.3*graph_size, r=0.3*graph_size, t=0.3*graph_size, b=0.3*graph_size))\n",
" \n",
" checks = VBox([Label('Final checks'),check1, check2, check3]) \n",
" checks.layout = Layout(border='dashed 1px gray', margin='30px 0px 50px 40px', padding='5px 5px 5px 5px')\n",
" \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=\"<b>Usable area </b>\", 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=3.2*graph_size, height=3.0*graph_size, margin =dict(l=0.3*graph_size, r=0.3*graph_size, t=0.1*graph_size, 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), opt.cameralengths()])\n",
"\n",
" align6 = dict(layout=Layout(width=str(int(graph_size/100*62))+'px') , style = {'description_width': '0px','button_width': str(int(graph_size/100*40))+'px'}, disabled=True,)\n",
" align7 = dict(layout=Layout(width='140px') , style = {'description_width': '0px','button_width': \"120px\"}, disabled=True,)\n",
" \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",
" data_tuple = tuple(tab[0:4,int(i)]) + (\"\",tab[4,int(i)])\n",
" \n",
" \n",
" icons=['','','','','']\n",
" \n",
" if cl_check_nbf == True: \n",
" if data[2] > 6:\n",
" button_style = 'warning' \n",
" \n",
" if cl_check_laa == True:\n",
" if data[1] > 1000*np.radians(10):\n",
" button_style = 'danger' \n",
" \n",
" if cl_check_pix == True: \n",
" if data[0] > step_size_corr*10:\n",
" button_style = 'danger'\n",
" \n",
" if cl_check_def == True: \n",
" if data[3] < defocus:\n",
" button_style = 'danger'\n",
" \n",
" if cl_check_1bf == True: \n",
" if data[2] < 1:\n",
" button_style = 'danger' \n",
" \n",
" if cl == data[4]:\n",
" icons=['','','','','check']\n",
" \n",
" \n",
" dictionary[i] = ToggleButtons(options=data_tuple, button_style=button_style, icons = icons, **align6)\n",
" \n",
" legend = ToggleButtons(options=['Ptycho pix. (Å)','Det. cover (mrad)','Det. cover (α)', 'Max. def. (nm)','Selected by CL ','Nominal CL (cm)',], \n",
" tooltips=['Dependent on: detector, beam energy, camera length',\n",
" 'Dependent on: detector, camera length',\n",
" 'Dependent on: detector, camera length, probe semi-angle',\n",
" 'Dependent on: detector, binning, beam energy, camera length, zero defocus beam diameter, probe semi-angle',\n",
" 'Just nominal camera length',\n",
" ],button_style='', **align7) \n",
" \n",
" \n",
" cltab = 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 = VBox([Label('Chose your reconstructed ptychographic pixe size, check needed defocus and set CL'), cltab])\n",
" cltab.layout = Layout(border='solid 0px gray',margin='10px 10px 10px 110px', padding='10px 10px 10px 10px')\n",
" \n",
" \n",
" \n",
" ### SHOWING ###\n",
" sample_overlap = HBox([go.FigureWidget(fig5), go.FigureWidget(fig7), go.FigureWidget(fig6)]) \n",
" right_column = VBox([HBox([go.FigureWidget(fig),checks]), sample_overlap, cltab]) \n",
" left_column = VBox([go.FigureWidget(fig8)]) \n",
" \n",
" \n",
" total = HBox([left_column, right_column])\n",
" total.layout = Layout(border='solid 0px gray',margin='10px 10px 10px 40px', padding='10px 10px 10px 10px')\n",
" \n",
" display(total)\n",
" \n",
" return \n",
" \n",
"gui = interactive_output(ptycho_interact, {\"beam\": beam, \"aperture\": aperture,\"aperture_res\": aperture_res,\"aperture_res2\": aperture_res2, \"probe\": probe, \"cl\": cl, \"matrix\": matrix, \"defocus\": defocus, \"mag\": mag, \"camera\": camera,\n",
" \"binning\": binning, \"dwell_time\": dwell_time, \"restriction\": restriction, \"method\": method, \"ctf_xaxis\": ctf_xaxis,\n",
" \"cl_check_laa\":cl_check_laa, \"cl_check_pix\": cl_check_pix, \"cl_check_def\": cl_check_def, \"cl_check_1bf\": cl_check_1bf, \"cl_check_nbf\": cl_check_nbf, \"graph_size\": graph_size}) \n",
"\n"
]
},
{
"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",
"\n",
"For more info go to https://gitlab.psi.ch/em-and-diffraction/low-dose-electron-ptychography/ptychoscopy/-/wikis/ptychoScopy\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(HBox([controls, VBox([small_controls, gui]) ]))\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 762,
"id": "7937f054-fcd0-4e67-a20f-7696f5903a94",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "8cc6b115d9154a9dbe2bd574fa3755cc",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(GridBox(children=(Button(description='STEM control panel', layout=Layout(grid_area='header', wi…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"display(HBox([controls, VBox([small_controls, gui]) ]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b939697a-2ab0-49b2-8c73-555bb62a2e0d",
"metadata": {},
"outputs": [],
"source": [
"# import numpy as np\n",
"# print(\"Numpy version \", np.__version__)\n",
"# import scipy\n",
"# print(\"scipy version \", scipy.__version__)\n",
"# import plotly\n",
"# print(\"Plotly version \", plotly.__version__)\n",
"# import pandas\n",
"# print(\"Pandas version \", pandas.__version__)\n",
"# import IPython\n",
"# print(\"IPython version \", IPython.__version__)\n",
"# import ipywidgets\n",
"# print(\"Ipywidgets version \", ipywidgets.__version__)"
]
}
],
"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
}