From e0a2d7d64f593a41411d200cf3c9a4ed89a15b4f Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Mon, 7 Oct 2024 16:23:31 +0100 Subject: [PATCH 1/9] config updates, align frames setup --- .github/workflows/publish_and_tag.yml | 2 +- perfectem/common.py | 9 +++++++-- perfectem/config.py | 12 ++++++------ perfectem/scripts/gold_diffraction.py | 4 ++-- perfectem/scripts/info_limit.py | 8 ++++---- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/publish_and_tag.yml b/.github/workflows/publish_and_tag.yml index 5ebe085..03a9c0d 100755 --- a/.github/workflows/publish_and_tag.yml +++ b/.github/workflows/publish_and_tag.yml @@ -18,7 +18,7 @@ jobs: python -m pip install --upgrade pip pip install setuptools twine pip install numpy scipy matplotlib mrcfile - - name: Build and publish + - name: Publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} diff --git a/perfectem/common.py b/perfectem/common.py index 0bf7cd8..c59e019 100644 --- a/perfectem/common.py +++ b/perfectem/common.py @@ -258,7 +258,8 @@ def setup_beam(self, mag: int, spot: int, beamsize: float, def setup_area(self, exp: float, binning: int, area: str = "F", preset: str = "F", - mode: Optional[str] = None) -> None: + mode: Optional[int] = None, + frames: Optional[bool] = False) -> None: """ Setup camera settings for a certain preset. """ logging.info(f"Setting camera: preset={preset}, exp={exp}, binning={binning}, area={area}") @@ -271,7 +272,11 @@ def setup_area(self, exp: float, binning: int, sem.NoMessageBoxOnError() try: sem.SetK2ReadMode(preset, camera_mode) # linear=0, counting=1 - sem.SetDoseFracParams(preset, 0, 0, 0) # no frames + if frames: + sem.SetFrameTime(preset, 0.000001) # will be fixed by SEM to a min number + sem.SetDoseFracParams(preset, 1, 0, 1, 1, 0) # align frames with SEM plugin + else: + sem.SetDoseFracParams(preset, 0, 0, 0, 0, 0) # no frames except sem.SEMerror or sem.SEMmoduleError: pass sem.NoMessageBoxOnError(0) diff --git a/perfectem/config.py b/perfectem/config.py index a69b3aa..f5060e6 100644 --- a/perfectem/config.py +++ b/perfectem/config.py @@ -33,7 +33,7 @@ krios2_falcon4i = { "StageDrift": {"beam": 1.1, "spot": 4, "mag": 96000, "exp": 0.5, "binning": 2}, "Anisotropy": {"beam": 1.1, "spot": 4, "mag": 96000, "exp": 0.5, "binning": 2}, - "InfoLimit": {"beam": 0.4, "spot": 3, "mag": 600000, "exp": 1, "binning": 2, "defocus": -0.5, "spec": 0.14}, + "InfoLimit": {"beam": 0.4, "spot": 2, "mag": 600000, "exp": 1, "binning": 2, "defocus": -0.5, "spec": 0.14}, "ThonRings": {"beam": 0.7, "spot": 2, "mag": 250000, "exp": 1, "binning": 2, "defocus": -1, "spec": 0.33}, "PointRes": {"beam": 0.7, "spot": 2, "mag": 380000, "exp": 2, "binning": 2, "defocus": -0.087, "spec": 0.20}, "GoldDiffr": {"beam": 0.4, "spot": 3, "mag": 600000, "exp": 1, "binning": 2, "defocus": -0.5, "spec": 0.1}, @@ -47,10 +47,10 @@ krios1_k3 = { "StageDrift": {"beam": 1.1, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, "Anisotropy": {"beam": 1.1, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, - "InfoLimit": {"beam": 1.01, "spot": 2, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.14}, + "InfoLimit": {"beam": 0.42, "spot": 2, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.14}, "ThonRings": {"beam": 0.7, "spot": 2, "mag": 270000, "exp": 1, "binning": 2, "defocus": -1, "spec": 0.33}, "PointRes": {"beam": 0.7, "spot": 2, "mag": 350000, "exp": 1, "binning": 2, "defocus": -0.087, "spec": 0.20}, - "GoldDiffr": {"beam": 1.01, "spot": 2, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.1}, + "GoldDiffr": {"beam": 0.42, "spot": 2, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.1}, "C2Fringes": {"beam": 0.42, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 1, "defocus": -1.0}, "TiltAxis": {"beam": 1.1, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, "GainRef": {"beam": 1.1, "spot": 5, "mag": 105000, "exp": 1, "binning": 1}, @@ -61,7 +61,7 @@ krios3_k3 = { "StageDrift": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, "Anisotropy": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, - "InfoLimit": {"beam": 1.02, "spot": 3, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.14}, + "InfoLimit": {"beam": 1.02, "spot": 2, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.14}, "ThonRings": {"beam": 1.02, "spot": 3, "mag": 270000, "exp": 1, "binning": 2, "defocus": -1, "spec": 0.33}, "PointRes": {"beam": 1.02, "spot": 3, "mag": 350000, "exp": 1, "binning": 2, "defocus": -0.087, "spec": 0.20}, "GoldDiffr": {"beam": 1.02, "spot": 3, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.1}, @@ -89,10 +89,10 @@ glacios_falcon3 = { "StageDrift": {"beam": 43.808, "spot": 2, "mag": 92000, "exp": 0.5, "binning": 2}, "Anisotropy": {"beam": 43.808, "spot": 2, "mag": 92000, "exp": 1, "binning": 2}, - "InfoLimit": {"beam": 38.059, "spot": 2, "mag": 400000, "exp": 1.5, "binning": 1, "defocus": -0.35, "spec": 0.23}, + "InfoLimit": {"beam": 38.059, "spot": 2, "mag": 400000, "exp": 1.5, "binning": 1, "defocus": -0.5, "spec": 0.23}, "ThonRings": {"beam": 38.016, "spot": 2, "mag": 240000, "exp": 1, "binning": 2, "defocus": -2, "spec": 0.37}, "PointRes": {"beam": 38.059, "spot": 2, "mag": 400000, "exp": 1, "binning": 2, "defocus": -0.082, "spec": 0.24}, - "GoldDiffr": {"beam": 38.059, "spot": 2, "mag": 650000, "exp": 1, "binning": 2, "defocus": -0.3, "spec": 0.2}, + "GoldDiffr": {"beam": 38.059, "spot": 2, "mag": 650000, "exp": 1, "binning": 2, "defocus": -0.5, "spec": 0.2}, "C2Fringes": {"beam": 38.338, "spot": 5, "mag": 92000, "exp": 0.1, "binning": 1}, "TiltAxis": {"beam": 43.808, "spot": 2, "mag": 92000, "exp": 0.5, "binning": 2}, "GainRef": {"beam": 43.808, "spot": 2, "mag": 92000, "exp": 1, "binning": 1}, diff --git a/perfectem/scripts/gold_diffraction.py b/perfectem/scripts/gold_diffraction.py index 7570c32..c6d87f4 100644 --- a/perfectem/scripts/gold_diffraction.py +++ b/perfectem/scripts/gold_diffraction.py @@ -54,7 +54,7 @@ def __init__(self, log_fn: str = "gold_diffr", **kwargs: Any) -> None: self.specification = kwargs.get("spec", 0.1) # nm def _run(self) -> None: - self.change_aperture("c2", 150) + self.change_aperture("c2", 50) self.setup_beam(self.mag, self.spot, self.beam_size) sem.Pause("Please center the beam, roughly focus the image, check beam tilt pp and rotation center") self.setup_beam(self.mag, self.spot, self.beam_size) @@ -62,7 +62,7 @@ def _run(self) -> None: self.setup_area(exp=0.5, binning=4, preset="R") self.autofocus(self.defocus, 0.05, do_coma=True, high_mag=True) self.check_drift() - self.setup_area(self.exp, self.binning, preset="R") + self.setup_area(self.exp, self.binning, preset="R", frames=True) self.check_before_acquire() if self.CAMERA_HAS_DIVIDEBY2: diff --git a/perfectem/scripts/info_limit.py b/perfectem/scripts/info_limit.py index d91b738..ebe49bd 100644 --- a/perfectem/scripts/info_limit.py +++ b/perfectem/scripts/info_limit.py @@ -40,7 +40,7 @@ class InfoLimit(BaseSetup): Desc: Take two images with a small image shift (2 nm), add them together and calculate FFT. You should observe Young fringes going up to 1 A. - Specification (Krios): 0.14 nm at 0 tilt, 0.23 nm at 70 deg. tilt. + Specification (Krios None: - self.change_aperture("c2", 150) + self.change_aperture("c2", 50) self.setup_beam(self.mag, self.spot, self.beam_size, check_dose=False) sem.Pause("Please center the beam, roughly focus the image, check beam tilt pp and rotation center") self.setup_beam(self.mag, self.spot, self.beam_size) @@ -69,7 +69,7 @@ def _run(self) -> None: self.setup_area(exp=0.5, binning=4, preset="F") self.autofocus(self.defocus, 0.05, do_coma=True, high_mag=True) self.check_drift() - self.setup_area(self.exp, self.binning, preset="R") + self.setup_area(self.exp, self.binning, preset="R", frames=True) self.check_before_acquire() logging.info(f"Taking two images with {self.shift} um image shift difference") From 0c3221da46006b27578cd3748055032ecfc4bfb6 Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Tue, 8 Oct 2024 16:45:27 +0100 Subject: [PATCH 2/9] falcon4 works, frame alignment added, config update, playing with ACF --- CHANGES.txt | 4 ++++ perfectem/common.py | 8 ++++---- perfectem/config.py | 18 +++++++++--------- perfectem/scripts/afis.py | 4 +--- perfectem/scripts/eucentricity.py | 4 ++-- perfectem/scripts/gain_reference.py | 19 ++++++++++++------- 6 files changed, 32 insertions(+), 25 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c8812f7..e811416 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +0.9.5: + - afis # of checks reduced to 4 + - added frame alignment with SEM plugin, enabled for Young fringes and gold diffraction tests + - configs updated 0.9.4: - add atlas realignment test - update Delay command diff --git a/perfectem/common.py b/perfectem/common.py index c59e019..f1b765d 100644 --- a/perfectem/common.py +++ b/perfectem/common.py @@ -125,12 +125,12 @@ def select_camera(self) -> None: sem.SelectCamera(camera_num) self.CAMERA_NUM = camera_num cam_name = sem.ReportCameraName(self.CAMERA_NUM) - if "Falcon" in cam_name: - self.CAMERA_MODE = 0 - self.CAMERA_HAS_DIVIDEBY2 = False - elif "K2" or "K3" in cam_name: + if "K2" or "K3" in cam_name: self.CAMERA_MODE = 1 # always counting self.CAMERA_HAS_DIVIDEBY2 = True + if "Falcon 4" in cam_name: + self.CAMERA_MODE = 1 # always counting + self.CAMERA_HAS_DIVIDEBY2 = False _, _, mode = sem.ReportMag() if mode == 1: # EFTEM diff --git a/perfectem/config.py b/perfectem/config.py index f5060e6..66fd9f2 100644 --- a/perfectem/config.py +++ b/perfectem/config.py @@ -31,17 +31,17 @@ # beam size in microns (Krios, 3-cond. lenses) or percents (2-cond. lenses) krios2_falcon4i = { - "StageDrift": {"beam": 1.1, "spot": 4, "mag": 96000, "exp": 0.5, "binning": 2}, - "Anisotropy": {"beam": 1.1, "spot": 4, "mag": 96000, "exp": 0.5, "binning": 2}, - "InfoLimit": {"beam": 0.4, "spot": 2, "mag": 600000, "exp": 1, "binning": 2, "defocus": -0.5, "spec": 0.14}, + "StageDrift": {"beam": 0.7, "spot": 7, "mag": 96000, "exp": 1, "binning": 2}, + "Anisotropy": {"beam": 0.7, "spot": 7, "mag": 96000, "exp": 1, "binning": 2}, + "InfoLimit": {"beam": 0.42, "spot": 2, "mag": 740000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.14}, "ThonRings": {"beam": 0.7, "spot": 2, "mag": 250000, "exp": 1, "binning": 2, "defocus": -1, "spec": 0.33}, "PointRes": {"beam": 0.7, "spot": 2, "mag": 380000, "exp": 2, "binning": 2, "defocus": -0.087, "spec": 0.20}, - "GoldDiffr": {"beam": 0.4, "spot": 3, "mag": 600000, "exp": 1, "binning": 2, "defocus": -0.5, "spec": 0.1}, - "C2Fringes": {"beam": 0.42, "spot": 6, "mag": 96000, "exp": 0.5, "binning": 1}, - "TiltAxis": {"beam": 1.1, "spot": 5, "mag": 75000, "exp": 0.5, "binning": 2}, - "GainRef": {"beam": 1.1, "spot": 7, "mag": 96000, "exp": 1, "binning": 1}, - "AFIS": {"beam": 1.1, "spot": 7, "mag": 96000, "exp": 1, "binning": 2, "defocus": -2, "max_imgsh": 12.0, "spec": (750, 10)}, # specs: (coma in nm, astig in nm) - "Eucentricity": {"beam": 1.1, "spot": 5, "mag": 75000, "exp": 0.5, "binning": 2, "spec": (2, 4)}, + "GoldDiffr": {"beam": 0.42, "spot": 2, "mag": 740000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.1}, + "C2Fringes": {"beam": 0.7, "spot": 7, "mag": 96000, "exp": 1, "binning": 1}, + "TiltAxis": {"beam": 0.9, "spot": 7, "mag": 75000, "exp": 1, "binning": 2}, + "GainRef": {"beam": 0.7, "spot": 7, "mag": 96000, "exp": 1, "binning": 1}, + "AFIS": {"beam": 0.7, "spot": 7, "mag": 96000, "exp": 1, "binning": 2, "defocus": -2, "max_imgsh": 12.0, "spec": (750, 10)}, # specs: (coma in nm, astig in nm) + "Eucentricity": {"beam": 0.9, "spot": 7, "mag": 75000, "exp": 1, "binning": 2, "spec": (2, 4)}, } krios1_k3 = { diff --git a/perfectem/scripts/afis.py b/perfectem/scripts/afis.py index c5fb000..6d6aa94 100644 --- a/perfectem/scripts/afis.py +++ b/perfectem/scripts/afis.py @@ -68,12 +68,10 @@ def _run(self) -> None: self.autofocus(self.defocus, 0.05, do_ast=True, do_coma=True) bis_positions = [ - (-self.shift, -self.shift), (-self.shift, 0), (self.shift, 0), (0, -self.shift), - (0, self.shift), - (self.shift, self.shift) + (0, self.shift) ] res = [] diff --git a/perfectem/scripts/eucentricity.py b/perfectem/scripts/eucentricity.py index d9776ed..5417971 100644 --- a/perfectem/scripts/eucentricity.py +++ b/perfectem/scripts/eucentricity.py @@ -126,7 +126,7 @@ def _run(self) -> None: logging.info(f"Current stage position: {x0}, {y0}, {z0}") results.append([0, 0, 0, 0]) - for tilt in range(-5, -75, -self.increment): + for tilt in range(-5, -65, -self.increment): res = self._tilt(tilt, x0, y0) if res is not None: results.append(res) @@ -134,7 +134,7 @@ def _run(self) -> None: sem.TiltTo(0) sem.Delay(3, "s") - for tilt in range(5, 75, self.increment): + for tilt in range(5, 65, self.increment): res = self._tilt(tilt, x0, y0) if res is not None: results.append(res) diff --git a/perfectem/scripts/gain_reference.py b/perfectem/scripts/gain_reference.py index f4ddc1f..d0c33c1 100644 --- a/perfectem/scripts/gain_reference.py +++ b/perfectem/scripts/gain_reference.py @@ -26,6 +26,7 @@ from typing import Any import numpy as np +from scipy.signal import correlate import serialem as sem from ..common import BaseSetup @@ -42,17 +43,21 @@ def __init__(self, log_fn: str = "gain_ref", **kwargs: Any) -> None: super().__init__(log_fn, **kwargs) def _run(self) -> None: - sem.Pause("Please move stage to an empty area") self.setup_beam(self.mag, self.spot, self.beam_size) - sem.Pause("Please center the beam") + sem.Pause("Please move stage to an empty area and center the beam") self.setup_beam(self.mag, self.spot, self.beam_size) self.setup_area(self.exp, self.binning, preset="R") self.check_before_acquire() sem.Record() data = np.asarray(sem.bufferImage("A")).astype("int16") - data_ft = np.fft.fft(data, axis=1) - data_ac = np.fft.ifft(data_ft * np.conjugate(data_ft), axis=1).real - - fig, axes = plot_fft_and_text(data_ac) - fig.savefig(f"gain_check_{self.timestamp}.jpg") + # Subtract the mean to center the data + data = data - np.mean(data) + acf = correlate(data, data, method='fft') + # Normalize the ACF + acf /= np.max(acf) + # Get the center of the ACF + acf = acf[acf.shape[0] // 2:, acf.shape[1] // 2:] + + fig, axes = plot_fft_and_text(acf) + fig.savefig(f"gain_check_acf_{self.timestamp}.png") From 54e74bf1c28a83b491c77a8c4632c4a4ec19203c Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Wed, 9 Oct 2024 13:20:44 +0100 Subject: [PATCH 3/9] reset img shifts at the start - skip auto-stigmate very close to focus, because SEM will increase the defocus - add wait for drift for eucentricity and tilt axis tests --- CHANGES.txt | 3 +++ perfectem/common.py | 6 +++--- perfectem/scripts/afis.py | 5 +++-- perfectem/scripts/atlas_realignment.py | 3 ++- perfectem/scripts/eucentricity.py | 1 + perfectem/scripts/tilt_axis.py | 6 +++--- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e811416..1ef9d44 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,9 @@ - afis # of checks reduced to 4 - added frame alignment with SEM plugin, enabled for Young fringes and gold diffraction tests - configs updated + - reset img shifts at the start + - skip auto-stigmate very close to focus, because SEM will increase the defocus + - add wait for drift for eucentricity and tilt axis tests 0.9.4: - add atlas realignment test - update Delay command diff --git a/perfectem/common.py b/perfectem/common.py index f1b765d..61ba3a4 100644 --- a/perfectem/common.py +++ b/perfectem/common.py @@ -170,6 +170,7 @@ def run(self) -> None: logging.info(f"Starting script {test_name} {start_time.strftime('%d/%m/%Y %H:%M:%S')}") try: + sem.SetImageShift(0, 0) if abs(sem.ReportTiltAngle()) > 0.1: sem.TiltTo(0) self._run() @@ -183,7 +184,6 @@ def run(self) -> None: def check_eps(self) -> None: """ Check max eps after setup beam but before area setup. """ - # TODO: verify minimum dose rate for Falcon3/4 logging.info("Checking dose rate...") old_exp, _ = sem.ReportExposure("F") @@ -202,7 +202,7 @@ def check_eps(self) -> None: spot += 1 if spot != int(sem.ReportSpotSize()) and spot < 12: - logging.info(f"Increasing spot size to {spot} to reduce dose rate below 120 eps") + logging.info(f"Increasing spot size to {spot} to reduce dose rate below 200 eps") sem.SetSpotSize(spot) # Restore previous settings @@ -356,7 +356,7 @@ def autofocus(target: float, precision: float = 0.05, logging.info(f"Autofocusing to {target} um...") sem.AutoFocus() - if high_mag: + if high_mag and target < -0.7: # fix astigmatism again, closer to focus sem.FixAstigmatismByCTF() diff --git a/perfectem/scripts/afis.py b/perfectem/scripts/afis.py index 6d6aa94..4adb5ab 100644 --- a/perfectem/scripts/afis.py +++ b/perfectem/scripts/afis.py @@ -57,9 +57,10 @@ def _mrad_to_nm(mrad): return mrad_to_invA(mrad, sem.ReportHighVoltage() * 1000) * 10 def _run(self) -> None: - sem.Pause("Open EPU and set Acquisition mode = Faster in the Session Setup. Also change C2 aperture to 50 um") + self.change_aperture("c2", 50) self.setup_beam(self.mag, self.spot, self.beam_size, check_dose=False) - sem.Pause("Please center the beam, roughly focus the image, check beam tilt pp and rotation center") + sem.Pause("Open EPU and set Acquisition mode = Faster in the Session Setup. " + "Center the beam, roughly focus the image, check beam tilt pp and rotation center") self.setup_beam(self.mag, self.spot, self.beam_size) self.setup_area(self.exp, self.binning, preset="R") self.setup_area(exp=1, binning=2, preset="F") diff --git a/perfectem/scripts/atlas_realignment.py b/perfectem/scripts/atlas_realignment.py index b790214..da92e43 100644 --- a/perfectem/scripts/atlas_realignment.py +++ b/perfectem/scripts/atlas_realignment.py @@ -87,7 +87,8 @@ def _run(self) -> None: if sem.ReportSlotStatus(grid) != 0: raise RuntimeError(f"Failed to load grid {grid}") sem.MoveStageTo(0, 0) - self.change_aperture("c2", 150) + if not self.SCOPE_HAS_C3: # For Talos / Glacios + self.change_aperture("c2", 150) self.setup_beam(self.mag, self.spot, self.beam_size, check_dose=False) self.setup_area(self.exp, self.binning, preset="R") self.check_before_acquire() diff --git a/perfectem/scripts/eucentricity.py b/perfectem/scripts/eucentricity.py index 5417971..888afa3 100644 --- a/perfectem/scripts/eucentricity.py +++ b/perfectem/scripts/eucentricity.py @@ -52,6 +52,7 @@ def _tilt(tilt, x0, y0) -> Optional[List[float]]: sem.TiltTo(tilt) logging.info(f"Tilting to {tilt} deg.") x, y, z = sem.ReportStageXYZ() + sem.DriftWaitTask(5.0, "A", 180, 1, 1, "F") sem.AutoFocus(-1) defocus, *_ = sem.ReportAutoFocus() if sem.ReportMeanCounts("A") < 5: # avoid grid bars diff --git a/perfectem/scripts/tilt_axis.py b/perfectem/scripts/tilt_axis.py index 8c7f55c..b31a118 100644 --- a/perfectem/scripts/tilt_axis.py +++ b/perfectem/scripts/tilt_axis.py @@ -47,7 +47,7 @@ class TiltAxis(BaseSetup): def __init__(self, log_fn: str = "tilt_axis", **kwargs: Any): super().__init__(log_fn, **kwargs) self.increment = 5 # tilt step - self.maxTilt = 15 # maximum +/- tilt angle + self.maxTilt = 25 # maximum +/- tilt angle self.offset = 5 # +/- offset for measured positions in microns from tilt axis # (also accepts lists e.g. [2, 4, 6]) @@ -55,14 +55,14 @@ def __init__(self, log_fn: str = "tilt_axis", **kwargs: Any): def _dz(alpha: float, y0: float) -> Any: return y0 * np.tan(np.radians(-alpha)) - @staticmethod - def _tilt(tilt: int, offsets: List[int], + def _tilt(self, tilt: int, offsets: List[int], focus0: List[float], focus: List[List], angles: List[float]) -> None: sem.TiltTo(tilt) for i in range(len(offsets)): sem.ImageShiftByMicrons(0, offsets[i]) + sem.DriftWaitTask(5.0, "A", 180, 1, 1, "F") sem.AutoFocus(-1) defocus, *_ = sem.ReportAutoFocus() focus[i].append(float(defocus)) From 115c973bbcdf290c935ffcb6e5fbcbb2a9bc83f9 Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Tue, 22 Oct 2024 16:28:40 +0100 Subject: [PATCH 4/9] working --- perfectem/__init__.py | 55 ++++++---- perfectem/common.py | 73 ++++++------ perfectem/config.py | 14 +-- perfectem/gui.py | 165 ++++++++++++++++++++++++++++ perfectem/scripts/gain_reference.py | 12 +- setup.py | 7 +- 6 files changed, 249 insertions(+), 77 deletions(-) create mode 100644 perfectem/gui.py diff --git a/perfectem/__init__.py b/perfectem/__init__.py index d0edcce..0bb74c9 100644 --- a/perfectem/__init__.py +++ b/perfectem/__init__.py @@ -1,3 +1,5 @@ +#!python +# -*- coding: utf-8 -*- # ************************************************************************** # * # * Authors: Grigory Sharov (gsharov@mrc-lmb.cam.ac.uk) [1] @@ -30,30 +32,30 @@ from .config import microscopes -__version__ = '0.9.4' +__version__ = '0.9.5' -tests = ( - ("AFIS", "AFIS validation"), - ("Anisotropy", "Magnification anisotropy"), - ("C2Fringes", "C2 Fresnel fringes"), - ("Eucentricity", "Eucentricity check"), - ("GainRef", "Gain reference check"), - ("GoldDiffr", "Gold diffraction"), - ("InfoLimit", "Information limit (Young fringes)"), - ("PointRes", "Point resolution"), - ("StageDrift", "Stage drift"), - ("ThonRings", "Thon rings"), - ("TiltAxis", "Tilt axis offset"), -) +tests = { + "AFIS": "AFIS validation", + "Anisotropy": "Magnification anisotropy", + "C2Fringes": "C2 Fresnel fringes", + "Eucentricity": "Eucentricity check", + "GainRef": "Gain reference check", + "GoldDiffr": "Gold diffraction", + "InfoLimit": "Information limit (Young fringes)", + "PointRes": "Point resolution", + "StageDrift": "Stage drift", + "ThonRings": "Thon rings", + "TiltAxis": "Tilt axis offset" +} def show_all_tests(print_docstr: bool = False) -> None: """ Print list of tests, with an optional docstring for each. """ - for num, test in enumerate(tests, start=1): - print(f"\t[{num}] {test[1]}") + for i, item in enumerate(tests.items()): + print(f"\t[{i+1}] {item[1]}") if print_docstr: module = importlib.import_module("perfectem.scripts") - func = getattr(module, test[0]) + func = getattr(module, item[0]) print(func.__doc__) print("="*80) @@ -61,7 +63,7 @@ def show_all_tests(print_docstr: bool = False) -> None: def main(argv: Optional[List] = None) -> None: try: import serialem - except ImportError: + except ModuleNotFoundError: raise ImportError("This program must be run on the computer with SerialEM Python module") parser = argparse.ArgumentParser(description="This script launches selected TEM performance test") @@ -79,16 +81,23 @@ def main(argv: Optional[List] = None) -> None: raise IndexError("Wrong test number!") print("\nAvailable microscopes:") - for scope_num, scope in enumerate(microscopes, start=1): - print(f"\t[{scope_num}] {scope[0]}") + for scope_num, scope in enumerate(microscopes.keys(), start=1): + print(f"\t[{scope_num}] {scope}") scope_num = int(input("\nInput the microscope number: ").strip()) - 1 if scope_num not in range(len(microscopes)): raise IndexError("Wrong microscope number!") - scope_name = microscopes[scope_num][0] + scope_name = list(microscopes.keys())[scope_num] - func_name = tests[test_num][0] + func_name = list(tests.keys())[test_num] module = importlib.import_module("perfectem.scripts") func_object = getattr(module, func_name) print(func_object.__doc__) - func_object(scope_name=scope_name, **microscopes[scope_num][1][func_name]).run() + func_args = list(microscopes.values())[scope_num][func_name] + func_object(scope_name=scope_name, **func_args).run() + + +def main_gui() -> None: + from .gui import Application + gui = Application(tests, microscopes) + gui.create_widgets() diff --git a/perfectem/common.py b/perfectem/common.py index 61ba3a4..fc8aade 100644 --- a/perfectem/common.py +++ b/perfectem/common.py @@ -39,7 +39,8 @@ class BaseSetup: """ Initialization and common functions. """ - def __init__(self, log_fn: str, scope_name: str, **kwargs: Any) -> None: + def __init__(self, log_fn: str, scope_name: str, + camera_num: Optional[int] = None, **kwargs: Any) -> None: """ Setup logger, camera settings and common SerialEM params. """ sem.ConnectToSEM(SERIALEM_PORT, SERIALEM_IP) @@ -48,7 +49,7 @@ def __init__(self, log_fn: str, scope_name: str, **kwargs: Any) -> None: self.SCOPE_HAS_C3 = self.func_is_implemented("ReportIlluminatedArea") self.SCOPE_HAS_AUTOFILL = self.func_is_implemented("AreDewarsFilling") self.SCOPE_HAS_APER_CTRL = self.func_is_implemented("ReportApertureSize", 1) - self.CAMERA_NUM = 1 + self.CAMERA_NUM = camera_num or 1 self.CAMERA_HAS_DIVIDEBY2 = False self.CAMERA_MODE = 0 # linear self.DELAY = 3 @@ -60,7 +61,7 @@ def __init__(self, log_fn: str, scope_name: str, **kwargs: Any) -> None: f"hasAutofill={self.SCOPE_HAS_AUTOFILL}, " f"hasApertureControl={self.SCOPE_HAS_APER_CTRL}") - self.select_camera() + self.select_camera(camera_num) sem.ClearPersistentVars() sem.SetUserSetting("DriftProtection", 1, 1) @@ -101,40 +102,40 @@ def setup_log(self, log_fn: str) -> None: sem.ErrorsToLog() sem.SetDirectory(os.getcwd()) - def select_camera(self) -> None: + def select_camera(self, camera_num: Optional[int] = None) -> None: """ Choose camera to use. """ - - camera_num = 1 - camera_names = [] - while True: - name = sem.ReportCameraName(camera_num) - if name == "NOCAM": - break - else: - camera_names.append(name) - camera_num += 1 - - print("Choose camera to use with SerialEM:\n") - for i, c in enumerate(camera_names): - print(f"\t[{i + 1}] {c}") - - camera_num = int(input("\nInput the camera number: ").strip()) - if camera_num > len(camera_names) or camera_num < 1: - raise IndexError("Wrong camera number!") - else: - sem.SelectCamera(camera_num) - self.CAMERA_NUM = camera_num - cam_name = sem.ReportCameraName(self.CAMERA_NUM) - if "K2" or "K3" in cam_name: - self.CAMERA_MODE = 1 # always counting - self.CAMERA_HAS_DIVIDEBY2 = True - if "Falcon 4" in cam_name: - self.CAMERA_MODE = 1 # always counting - self.CAMERA_HAS_DIVIDEBY2 = False - - _, _, mode = sem.ReportMag() - if mode == 1: # EFTEM - sem.SetSlitIn(0) # retract slit + if camera_num is None: # enter from cmd, not GUI + camera_num = 1 + camera_names = [] + while True: + name = sem.ReportCameraName(camera_num) + if name == "NOCAM": + break + else: + camera_names.append(name) + camera_num += 1 + + print("Choose camera to use with SerialEM:\n") + for i, c in enumerate(camera_names): + print(f"\t[{i + 1}] {c}") + + camera_num = int(input("\nInput the camera number: ").strip()) + if camera_num > len(camera_names) or camera_num < 1: + raise IndexError("Wrong camera number!") + + sem.SelectCamera(camera_num) + self.CAMERA_NUM = camera_num + cam_name = sem.ReportCameraName(self.CAMERA_NUM) + if "K2" or "K3" in cam_name: + self.CAMERA_MODE = 1 # always counting + self.CAMERA_HAS_DIVIDEBY2 = True + if "Falcon 4" in cam_name: + self.CAMERA_MODE = 1 # always counting + self.CAMERA_HAS_DIVIDEBY2 = False + + _, _, mode = sem.ReportMag() + if mode == 1: # EFTEM + sem.SetSlitIn(0) # retract slit if not sem.ReportColumnOrGunValve(): print("Opening col. valves...") diff --git a/perfectem/config.py b/perfectem/config.py index 66fd9f2..eda96ab 100644 --- a/perfectem/config.py +++ b/perfectem/config.py @@ -101,10 +101,10 @@ "AtlasRealignment": {"beam": 100.0, "spot": 5, "mag": 170, "exp": 0.5, "binning": 2}, } -microscopes = ( - ("Krios_1", krios1_k3), - ("Krios_2", krios2_falcon4i), - ("Krios_3", krios3_k3), - ("Krios_4", krios4_F4i), - ("Glacios", glacios_falcon3), -) +microscopes = { + "Krios_1": krios1_k3, + "Krios_2": krios2_falcon4i, + "Krios_3": krios3_k3, + "Krios_4": krios4_F4i, + "Glacios": glacios_falcon3, +} diff --git a/perfectem/gui.py b/perfectem/gui.py new file mode 100644 index 0000000..4cf7f1d --- /dev/null +++ b/perfectem/gui.py @@ -0,0 +1,165 @@ +# ************************************************************************** +# * +# * Authors: Grigory Sharov (gsharov@mrc-lmb.cam.ac.uk) [1] +# * +# * [1] MRC Laboratory of Molecular Biology, MRC-LMB +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 3 of the License, or +# * (at your option) any later version. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; if not, write to the Free Software +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# * 02111-1307 USA +# * +# * All comments concerning this program package may be sent to the +# * e-mail address 'gsharov@mrc-lmb.cam.ac.uk' +# * +# ************************************************************************** + +import importlib +import tkinter as tk +import tkinter.ttk as ttk +from tkinter import messagebox +from typing import Optional + +from perfectem import __version__ +from perfectem.config import SERIALEM_PORT, SERIALEM_IP + + +class Application: + def __init__(self, tests: dict, microscopes: dict) -> None: + """ Check serialEM import and initialise vars. """ + self.root = tk.Tk() + self.root.title(f"PerfectEM v{__version__}") + self.root.resizable(False, False) + + try: + self.cameras = self.get_cameras() + except Exception as e: + self._handle_error(e) + + self.tests = tests + self.microscopes = microscopes + + self.scope_var = tk.StringVar() + self.camera_var = tk.StringVar() + self.test_var = tk.StringVar() + + def _handle_error(self, error: Exception) -> None: + """ Handle SerialEM failure gracefully. """ + self.root.overrideredirect(True) + self.root.withdraw() + self.show_message(msgtype="error", + text="This program must be run on the computer " + f"with SerialEM Python module.\n\n{str(error)}") + exit(1) + + def create_widgets(self) -> None: + """ Create the main GUI. """ + content = ttk.Frame(self.root) + content.grid(column=0, row=0) + content.columnconfigure(0, weight=1, uniform="tag") + content.columnconfigure(1, weight=2, uniform="tag") + + self._create_combobox(content, "Microscope", + self.scope_var, + list(self.microscopes.keys()), + 0) + self._create_combobox(content, "Camera", + self.camera_var, + self.cameras, + 1) + self._create_combobox(content, "Performance test", + self.test_var, + list(self.tests.values()), + 2) + + # Help button + help_btn = ttk.Button(content, text="?", width=1, cursor='hand2', + command=lambda: self.show_help(self.test_var)) + help_btn.grid(column=3, row=2, padx=5, pady=0) + + # Run button + args_btn = self.scope_var, self.camera_var, self.test_var + run_btn = ttk.Button(content, text="Run!", cursor='hand2', + command=lambda: self.run(*args_btn)) + run_btn.grid(column=0, row=3, pady=5, columnspan=4) + + self.root.focus_set() + self.root.mainloop() + + @staticmethod + def _create_combobox(parent, label_text, variable, values, row) -> None: + """Helper to create a label and combobox pair.""" + label = ttk.Label(parent, text=label_text) + label.grid(column=0, row=row, padx=5, pady=5) + + combo = ttk.Combobox(parent, textvariable=variable, state='readonly', values=values) + combo.grid(column=1, row=row, padx=5, pady=5, + sticky="we" if row == 2 else "w", + columnspan=2) + combo.current(0) + + @staticmethod + def show_message(msgtype: Optional[str] = "info", text: Optional[str] = None) -> None: + """ Show a message dialog. """ + if msgtype == "info": + messagebox.showinfo(message=text) + elif msgtype == "error": + messagebox.showerror(message=text) + + def show_help(self, test_var: tk.StringVar) -> None: + """ Show the test description help. """ + index = list(self.tests.values()).index(test_var.get()) + module = importlib.import_module("perfectem.scripts") + func = getattr(module, list(self.tests.keys())[index]) + + self.show_message(text=func.__doc__) + + def get_cameras(self): + """ Connect to SerialEM and get the camera list. """ + import serialem as sem + sem.ConnectToSEM(SERIALEM_PORT, SERIALEM_IP) + self.cameras = [] + camera_num = 1 + while True: + name = sem.ReportCameraName(camera_num) + if name == "NOCAM": + break + else: + self.cameras.append(name) + camera_num += 1 + sem.Exit(1) + + return self.cameras + + def run(self, + scope: tk.StringVar, + camera: tk.StringVar, + test: tk.StringVar) -> None: + """ Execute selected test. """ + scope, camera, test = scope.get(), camera.get(), test.get() + if not scope or not camera or not test: + self.show_message(msgtype="error", text="Please select a microscope and camera.") + return + + func_name = None + for item in self.tests.items(): + if item[1] == test: + func_name = item[0] + break + + self.root.destroy() + module = importlib.import_module("perfectem.scripts") + func_object = getattr(module, func_name) + func_object(scope_name=scope, + camera_num=self.cameras.index(camera)+1, + **self.microscopes[scope][func_name]).run() diff --git a/perfectem/scripts/gain_reference.py b/perfectem/scripts/gain_reference.py index d0c33c1..806dd10 100644 --- a/perfectem/scripts/gain_reference.py +++ b/perfectem/scripts/gain_reference.py @@ -26,7 +26,6 @@ from typing import Any import numpy as np -from scipy.signal import correlate import serialem as sem from ..common import BaseSetup @@ -48,16 +47,13 @@ def _run(self) -> None: self.setup_beam(self.mag, self.spot, self.beam_size) self.setup_area(self.exp, self.binning, preset="R") self.check_before_acquire() - sem.Record() + data = np.asarray(sem.bufferImage("A")).astype("int16") - # Subtract the mean to center the data data = data - np.mean(data) - acf = correlate(data, data, method='fft') - # Normalize the ACF - acf /= np.max(acf) - # Get the center of the ACF - acf = acf[acf.shape[0] // 2:, acf.shape[1] // 2:] + data_ft = np.fft.fft2(data) + data_acf = np.fft.ifft2(data_ft * np.conjugate(data_ft)) + acf = np.abs(data_acf) fig, axes = plot_fft_and_text(acf) fig.savefig(f"gain_check_acf_{self.timestamp}.png") diff --git a/setup.py b/setup.py index 3d819cc..c269029 100644 --- a/setup.py +++ b/setup.py @@ -90,9 +90,10 @@ def run(self): "dev": ["mypy"] }, python_requires='>=3.8', - entry_points={'console_scripts': ['perfectem=perfectem:main']}, + entry_points={'console_scripts': ['perfectem=perfectem:main'], + 'gui_scripts': ['perfectem-gui=perfectem:main_gui']}, project_urls={ 'Bug Reports': 'https://github.com/azazellochg/perfectem/issues', - 'Source': 'https://github.com/azazellochg/perfectem', - }, + 'Source': 'https://github.com/azazellochg/perfectem' + } ) From 6d1e095e5639880916e1fe6659330b99e6fa480b Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Tue, 22 Oct 2024 16:48:09 +0100 Subject: [PATCH 5/9] minor changes --- README.rst | 9 ++++++--- perfectem/__init__.py | 3 ++- perfectem/scripts/__init__.py | 15 ++++++++------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/README.rst b/README.rst index 484444b..321b382 100644 --- a/README.rst +++ b/README.rst @@ -69,10 +69,11 @@ Offline installation Available scripts ----------------- -- AFIS +- AFIS validation - Specification (Krios): coma < 750 nm, astigmatism < 10 nm for 5 um shift. Glacios: coma < 1200 nm, astigmatism < 15 nm for 6 um shift - Description: Measure residual beam tilt and astigmatism at different image shift positions while EPU is open. - Atlas realignment + - Specification: none - Description: Compare the shift and rotation between two atlases acquired when reloading the same grid. - Magnification anisotropy - Specification: <1% @@ -80,10 +81,10 @@ Available scripts - C2 Fresnel fringes - Specification: on FFI system there should be <5 fringes at 96 kx in nanoprobe close to focus - Description: Take a picture of a flood beam to see if the fringes from C2 aperture extend all the way to the center (non-FFI systems). -- Eucentricity +- Eucentricity check - Specification: <2 um in X/Y, <4 um defocus (Krios G2, G3, G3i) - Description: Estimate X,Y and defocus offset while tilting the stage. -- Gain reference +- Gain reference check - Specification: none - Description: Take a picture of a flood beam and check the auto-correlation image. - Gold diffraction @@ -115,3 +116,5 @@ First, have a look at **config.py**: edit *microscopes* dictionary and individua .. code-block:: python perfectem + +If you prefer button clicking over console, try `perfectem-gui`. The simple GUI requires Python built with tkinter support. diff --git a/perfectem/__init__.py b/perfectem/__init__.py index 0bb74c9..a7e76d2 100644 --- a/perfectem/__init__.py +++ b/perfectem/__init__.py @@ -36,12 +36,13 @@ tests = { "AFIS": "AFIS validation", + "AtlasRealignment": "Atlas realignment", "Anisotropy": "Magnification anisotropy", "C2Fringes": "C2 Fresnel fringes", "Eucentricity": "Eucentricity check", "GainRef": "Gain reference check", "GoldDiffr": "Gold diffraction", - "InfoLimit": "Information limit (Young fringes)", + "InfoLimit": "Information limit", "PointRes": "Point resolution", "StageDrift": "Stage drift", "ThonRings": "Thon rings", diff --git a/perfectem/scripts/__init__.py b/perfectem/scripts/__init__.py index 1a5e8b5..a7c6f22 100644 --- a/perfectem/scripts/__init__.py +++ b/perfectem/scripts/__init__.py @@ -24,14 +24,15 @@ # * # ************************************************************************** -from .stage_drift import StageDrift -from .mag_anisotropy import Anisotropy -from .info_limit import InfoLimit -from .thon_rings import ThonRings -from .point_resolution import PointRes -from .gold_diffraction import GoldDiffr from .afis import AFIS +from .atlas_realignment import AtlasRealignment +from .eucentricity import Eucentricity from .gain_reference import GainRef +from .gold_diffraction import GoldDiffr +from .info_limit import InfoLimit +from .mag_anisotropy import Anisotropy +from .point_resolution import PointRes from .spatial_coherence import C2Fringes +from .stage_drift import StageDrift +from .thon_rings import ThonRings from .tilt_axis import TiltAxis -from .eucentricity import Eucentricity From 8a5dab9b084ac0e43ec8279f3a2d6c09e184c88a Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Wed, 23 Oct 2024 14:37:42 +0100 Subject: [PATCH 6/9] update readme --- README.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 321b382..d1680d0 100644 --- a/README.rst +++ b/README.rst @@ -117,4 +117,10 @@ First, have a look at **config.py**: edit *microscopes* dictionary and individua perfectem -If you prefer button clicking over console, try `perfectem-gui`. The simple GUI requires Python built with tkinter support. +If you prefer clicking buttons over console, you can create a desktop script **PerfectEM.bat** that contains one line: + +.. code-block:: + + perfectem-gui + +PS. The simple GUI requires Python built with tkinter support. From 3ed6f0bde7a2e15db4e91b027d0f46f4dddcc2ff Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Fri, 25 Oct 2024 12:16:00 +0100 Subject: [PATCH 7/9] reduce drift threshold, defocus offset for auto-euc, update krios 4 config --- perfectem/common.py | 6 +++--- perfectem/config.py | 22 +++++++++++----------- perfectem/scripts/eucentricity.py | 2 +- perfectem/scripts/tilt_axis.py | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/perfectem/common.py b/perfectem/common.py index fc8aade..b892eab 100644 --- a/perfectem/common.py +++ b/perfectem/common.py @@ -285,7 +285,7 @@ def setup_area(self, exp: float, binning: int, logging.info("Setting camera: done!") @staticmethod - def euc_by_stage(fine: bool = False) -> None: + def euc_by_stage(fine: bool = True) -> None: """ Check FOV before running eucentricity by stage. """ min_fov = sem.ReportProperty("EucentricityCoarseMinField") # um pix = sem.ReportCurrentPixelSize("T") # nm, with binning @@ -296,9 +296,9 @@ def euc_by_stage(fine: bool = False) -> None: f"{area} um < EucentricityCoarseMinField={min_fov}, " "SerialEM will decrease the magnification automatically") sem.SetAbsoluteFocus(0) - sem.ChangeFocus(-50) + sem.ChangeFocus(-30) sem.Eucentricity(2 if fine else 1) - sem.ChangeFocus(50) + sem.ChangeFocus(30) def euc_by_beamtilt(self) -> None: """ Adapted from https://sphinx-emdocs.readthedocs.io/en/latest/serialEM-note-more-about-z-height.html#z-byv2-function """ diff --git a/perfectem/config.py b/perfectem/config.py index eda96ab..696fed0 100644 --- a/perfectem/config.py +++ b/perfectem/config.py @@ -73,17 +73,17 @@ } krios4_F4i = { - "StageDrift": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, - "Anisotropy": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, - "InfoLimit": {"beam": 1.2, "spot": 2, "mag": 350000, "exp": 1, "binning": 1, "defocus": -0.5, "spec": 0.12}, - "ThonRings": {"beam": 1.02, "spot": 3, "mag": 270000, "exp": 1, "binning": 2, "defocus": -1, "spec": 0.33}, - "PointRes": {"beam": 1.02, "spot": 3, "mag": 350000, "exp": 1, "binning": 2, "defocus": -0.087, "spec": 0.20}, - "GoldDiffr": {"beam": 1.02, "spot": 3, "mag": 710000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.1}, - "C2Fringes": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 1, "defocus": -1.0}, - "TiltAxis": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2}, - "GainRef": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 1, "binning": 1}, - "AFIS": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 1, "binning": 2, "defocus": -2, "max_imgsh": 12.0, "spec": (750, 10)}, - "Eucentricity": {"beam": 0.66, "spot": 5, "mag": 105000, "exp": 0.5, "binning": 2, "spec": (2, 4)}, + "StageDrift": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 2}, + "Anisotropy": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 2}, + "InfoLimit": {"beam": 0.32, "spot": 2, "mag": 910000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.12}, + "ThonRings": {"beam": 1.0, "spot": 3, "mag": 270000, "exp": 1, "binning": 2, "defocus": -1, "spec": 0.33}, + "PointRes": {"beam": 1.0, "spot": 3, "mag": 350000, "exp": 1, "binning": 2, "defocus": -0.087, "spec": 0.20}, + "GoldDiffr": {"beam": 0.32, "spot": 2, "mag": 910000, "exp": 3, "binning": 2, "defocus": -0.5, "spec": 0.1}, + "C2Fringes": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 1, "defocus": -1.0}, + "TiltAxis": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 2}, + "GainRef": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 1}, + "AFIS": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 2, "defocus": -2, "max_imgsh": 12.0, "spec": (750, 10)}, + "Eucentricity": {"beam": 0.8, "spot": 5, "mag": 130000, "exp": 1, "binning": 2, "spec": (2, 4)}, } glacios_falcon3 = { diff --git a/perfectem/scripts/eucentricity.py b/perfectem/scripts/eucentricity.py index 888afa3..d880467 100644 --- a/perfectem/scripts/eucentricity.py +++ b/perfectem/scripts/eucentricity.py @@ -52,7 +52,7 @@ def _tilt(tilt, x0, y0) -> Optional[List[float]]: sem.TiltTo(tilt) logging.info(f"Tilting to {tilt} deg.") x, y, z = sem.ReportStageXYZ() - sem.DriftWaitTask(5.0, "A", 180, 1, 1, "F") + sem.DriftWaitTask(2.0, "A", 180, 1, 1, "F") sem.AutoFocus(-1) defocus, *_ = sem.ReportAutoFocus() if sem.ReportMeanCounts("A") < 5: # avoid grid bars diff --git a/perfectem/scripts/tilt_axis.py b/perfectem/scripts/tilt_axis.py index b31a118..fcd372b 100644 --- a/perfectem/scripts/tilt_axis.py +++ b/perfectem/scripts/tilt_axis.py @@ -62,7 +62,7 @@ def _tilt(self, tilt: int, offsets: List[int], for i in range(len(offsets)): sem.ImageShiftByMicrons(0, offsets[i]) - sem.DriftWaitTask(5.0, "A", 180, 1, 1, "F") + sem.DriftWaitTask(2.0, "A", 180, 1, 1, "F") sem.AutoFocus(-1) defocus, *_ = sem.ReportAutoFocus() focus[i].append(float(defocus)) From 164789ef4e0c4ee1a99486672414e0e9565446bd Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Fri, 25 Oct 2024 13:09:30 +0100 Subject: [PATCH 8/9] print pix size --- CHANGES.txt | 1 + perfectem/scripts/afis.py | 2 +- perfectem/scripts/gold_diffraction.py | 2 +- perfectem/scripts/info_limit.py | 2 +- perfectem/scripts/point_resolution.py | 2 +- perfectem/scripts/thon_rings.py | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1ef9d44..4f6b89e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,7 @@ - reset img shifts at the start - skip auto-stigmate very close to focus, because SEM will increase the defocus - add wait for drift for eucentricity and tilt axis tests + - add simple GUI as an option 0.9.4: - add atlas realignment test - update Delay command diff --git a/perfectem/scripts/afis.py b/perfectem/scripts/afis.py index 4adb5ab..08e60fa 100644 --- a/perfectem/scripts/afis.py +++ b/perfectem/scripts/afis.py @@ -54,7 +54,7 @@ def __init__(self, log_fn: str = "afis", @staticmethod def _mrad_to_nm(mrad): - return mrad_to_invA(mrad, sem.ReportHighVoltage() * 1000) * 10 + return 0.1 / mrad_to_invA(mrad, sem.ReportHighVoltage() * 1000) def _run(self) -> None: self.change_aperture("c2", 50) diff --git a/perfectem/scripts/gold_diffraction.py b/perfectem/scripts/gold_diffraction.py index c6d87f4..c8db8c2 100644 --- a/perfectem/scripts/gold_diffraction.py +++ b/perfectem/scripts/gold_diffraction.py @@ -81,7 +81,7 @@ def _run(self) -> None: Measurement performed {pretty_date(get_time=True)} Microscope type {self.scope_name} - Recorded at magnification {self.mag // 1000} kx + Recorded at magnification {self.mag // 1000} kx ({round(pix, 3)} A/px) Defocus {self.defocus} um Camera used {sem.ReportCameraName(self.CAMERA_NUM)} diff --git a/perfectem/scripts/info_limit.py b/perfectem/scripts/info_limit.py index ebe49bd..7e89436 100644 --- a/perfectem/scripts/info_limit.py +++ b/perfectem/scripts/info_limit.py @@ -93,7 +93,7 @@ def _run(self) -> None: Measurement performed {pretty_date(get_time=True)} Microscope name {self.scope_name} - Recorded at magnification {self.mag // 1000} kx + Recorded at magnification {self.mag // 1000} kx ({round(pix, 3)} A/px) Defocus {self.defocus} um Camera used {sem.ReportCameraName(self.CAMERA_NUM)} diff --git a/perfectem/scripts/point_resolution.py b/perfectem/scripts/point_resolution.py index e4d31a5..931bc65 100644 --- a/perfectem/scripts/point_resolution.py +++ b/perfectem/scripts/point_resolution.py @@ -88,7 +88,7 @@ def _run(self) -> None: Measurement performed {pretty_date(get_time=True)} Microscope name {self.scope_name} - Recorded at magnification {self.mag // 1000} kx + Recorded at magnification {self.mag // 1000} kx ({round(pix, 3)} A/px) Defocus {int(self.defocus*1000)} nm Camera used {sem.ReportCameraName(self.CAMERA_NUM)} diff --git a/perfectem/scripts/thon_rings.py b/perfectem/scripts/thon_rings.py index ec583a1..2cfbce8 100644 --- a/perfectem/scripts/thon_rings.py +++ b/perfectem/scripts/thon_rings.py @@ -83,7 +83,7 @@ def _run(self) -> None: Measurement performed {pretty_date(get_time=True)} Microscope name {self.scope_name} - Recorded at magnification {self.mag // 1000} kx + Recorded at magnification {self.mag // 1000} kx ({round(pix, 3)} A/px) Defocus {self.defocus} um Camera used {sem.ReportCameraName(self.CAMERA_NUM)} From ee8a4cbb568cea2537ecd4150e32b18fee870c4a Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Fri, 25 Oct 2024 13:24:52 +0100 Subject: [PATCH 9/9] fix type hinting --- perfectem/gui.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/perfectem/gui.py b/perfectem/gui.py index 4cf7f1d..f6602ee 100644 --- a/perfectem/gui.py +++ b/perfectem/gui.py @@ -142,16 +142,16 @@ def get_cameras(self): return self.cameras def run(self, - scope: tk.StringVar, - camera: tk.StringVar, - test: tk.StringVar) -> None: + scopeVar: tk.StringVar, + cameraVar: tk.StringVar, + testVar: tk.StringVar) -> None: """ Execute selected test. """ - scope, camera, test = scope.get(), camera.get(), test.get() + scope, camera, test = scopeVar.get(), cameraVar.get(), testVar.get() if not scope or not camera or not test: self.show_message(msgtype="error", text="Please select a microscope and camera.") return - func_name = None + func_name = "" for item in self.tests.items(): if item[1] == test: func_name = item[0]