Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fb16dce
base typing improvements
thomasmfish Jun 6, 2025
be596fb
conversions type improvements
thomasmfish Jun 6, 2025
e357aad
Update MillingStrategy and MillingStrategyConfig
thomasmfish Jun 6, 2025
d1a0552
image_settings.path is expected to be of type Path
thomasmfish Jun 6, 2025
84116bb
Add some milling Optionals
thomasmfish Jun 6, 2025
3318914
Improve MIllingStrategy typing
thomasmfish Jun 6, 2025
e79f044
Fix acquire_images_after_milling typing
thomasmfish Jun 6, 2025
7c2d63e
Add missing milling_voltage argument to run_milling
thomasmfish Jun 6, 2025
42abe3a
Raise errors for unexpected types handling strategy widgets
thomasmfish Jun 6, 2025
5359dce
Handle if ref_image attribute is unset in StandardMillingStrategy
thomasmfish Jun 6, 2025
25a7831
Improve FibsemPatternSettings typing
thomasmfish Jun 6, 2025
0dd1d2f
Improve pattern typing (and fix dataclass default argument issue)
thomasmfish Jun 6, 2025
510eb9e
Smaller plotting type fixes
thomasmfish Jun 6, 2025
5d89856
Various typing improvements for structures
thomasmfish Jun 6, 2025
4c8641d
Change default Nones to "Unknown"s to match from_dict
thomasmfish Jun 6, 2025
235e2d4
Fix typing changes
thomasmfish Jun 9, 2025
806efb9
MillingStrategy.to_dict no longer an abstract method
thomasmfish Jun 9, 2025
c1806f1
Fix BasePattern inheritance issues
thomasmfish Jun 10, 2025
1a7dd25
Improve alignment typing
thomasmfish Jun 10, 2025
5982ff8
estimate_milling_time: simplify cross_section check
thomasmfish Jun 10, 2025
f49b99a
Handle to_dict and from_dict in FibsemPatternSettings
thomasmfish Jun 10, 2025
1fe3963
Use collections.abc.Generator type hint
thomasmfish Jun 10, 2025
2d9fad4
Update to newer typing style thanks to __future__.annotations
thomasmfish Jun 10, 2025
9ea26ff
Update more typing with __future__.annotations
thomasmfish Jun 10, 2025
86e4536
Type can also be updated to type
thomasmfish Jun 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fibsem/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def multi_step_alignment_v2(
microscope: FibsemMicroscope,
ref_image: FibsemImage,
beam_type: BeamType,
alignment_current: float = None,
alignment_current: Optional[float] = None,
steps: int = 3,
use_autocontrast: bool = False,
subsystem: Optional[str] = None,
Expand Down
9 changes: 6 additions & 3 deletions fibsem/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ def image_to_microscope_image_coordinates(
dy = float(-(coord.y - cy)) # neg = down
dx = float(coord.x - cx) # neg = left

point_m = convert_point_from_pixel_to_metres(Point(dx, dy), pixelsize)
point_m = convert_point_from_pixel_to_metres(Point(x=dx, y=dy), pixelsize)
# point_m = Point(dx, dy)._to_metres(pixel_size=pixelsize)

return point_m


def get_lamella_size_in_pixels(
img: FibsemImage, protocol: dict, use_trench_height: bool = False
) -> Tuple[int]:
) -> Tuple[int, int]:
"""Get the relative size of the lamella in pixels based on the hfw of the image.

Args:
Expand All @@ -53,6 +53,9 @@ def get_lamella_size_in_pixels(
Returns:
Tuple[int]: A tuple containing the height and width of the lamella in pixels.
"""
if img.metadata is None:
raise ValueError("Image has no metadata")

# get real size from protocol
lamella_width = protocol["lamella_width"]
lamella_height = protocol["lamella_height"]
Expand Down Expand Up @@ -89,7 +92,7 @@ def convert_metres_to_pixels(distance: float, pixelsize: float) -> int:
return int(distance / pixelsize)


def convert_pixels_to_metres(pixels: int, pixelsize: float) -> float:
def convert_pixels_to_metres(pixels: float, pixelsize: float) -> float:
"""
Convert a distance in pixels to metres based on a given pixel size.

Expand Down
4 changes: 2 additions & 2 deletions fibsem/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def setup_milling(self, mill_settings: FibsemMillingSettings) -> None:
pass

@abstractmethod
def run_milling(self, milling_current: float, asynch: bool) -> None:
def run_milling(self, milling_current: float, milling_voltage: float, asynch: bool) -> None:
pass

@abstractmethod
Expand Down Expand Up @@ -1079,7 +1079,7 @@ class ThermoMicroscope(FibsemMicroscope):
setup_milling(self, mill_settings: FibsemMillingSettings):
Configure the microscope for milling using the ion beam.

run_milling(self, milling_current: float, asynch: bool = False):
run_milling(self, milling_current: float, milling_voltage: float, asynch: bool = False):
Run ion beam milling using the specified milling current.

finish_milling(self, imaging_current: float):
Expand Down
71 changes: 37 additions & 34 deletions fibsem/milling/base.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,54 @@
from abc import ABC, abstractmethod
from copy import deepcopy
from dataclasses import dataclass, fields, field
from typing import List, Union, Dict, Any, Tuple, Optional
from dataclasses import dataclass, fields, field, asdict
from typing import List, Dict, Any, Tuple, Optional, Type, TypeVar, ClassVar, Generic

from fibsem.microscope import FibsemMicroscope
from fibsem.milling.config import MILLING_SPUTTER_RATE
from fibsem.milling.patterning.patterns2 import BasePattern as BasePattern, get_pattern as get_pattern
from fibsem.structures import FibsemMillingSettings, Point, MillingAlignment, ImageSettings, CrossSectionPattern
from fibsem.structures import FibsemMillingSettings, MillingAlignment, ImageSettings, CrossSectionPattern


TMillingStrategyConfig = TypeVar(
"TMillingStrategyConfig", bound="MillingStrategyConfig"
)
TMillingStrategy = TypeVar("TMillingStrategy", bound="MillingStrategy")


@dataclass
class MillingStrategyConfig(ABC):
"""Abstract base class for milling strategy configurations"""

def to_dict(self):
return {}
_advanced_attributes: ClassVar[Tuple[str, ...]] = ()

def to_dict(self) -> Dict[str, Any]:
return asdict(self)

@classmethod
def from_dict(
cls: Type[TMillingStrategyConfig], d: Dict[str, Any]
) -> TMillingStrategyConfig:
return cls(**d)

@staticmethod
def from_dict(d: dict) -> "MillingStrategyConfig":
return MillingStrategyConfig()

@property
def required_attributes(self) -> Tuple[str]:
return [field.name for field in fields(self)]

@property
def advanced_attributes(self) -> List[str]:
if hasattr(self, "_advanced_attributes"):
return self._advanced_attributes
return []
def required_attributes(self) -> Tuple[str, ...]:
return tuple(f.name for f in fields(self))

@dataclass
class MillingStrategy(ABC):

class MillingStrategy(ABC, Generic[TMillingStrategyConfig]):
"""Abstract base class for different milling strategies"""
name: str = "Milling Strategy"
config = MillingStrategyConfig()
name: str = "Milling Strategy"
config_class: Type[TMillingStrategyConfig]

def __init__(self, **kwargs):
pass

@abstractmethod
def to_dict(self):
def __init__(self, config: Optional[TMillingStrategyConfig] = None):
self.config: TMillingStrategyConfig = config or self.config_class()

def to_dict(self) -> dict[str, Any]:
return {"name": self.name, "config": self.config.to_dict()}
@staticmethod
@abstractmethod
def from_dict(d: dict) -> "MillingStrategy":
pass

@classmethod
def from_dict(cls: Type[TMillingStrategy], d: dict) -> TMillingStrategy:
config=cls.config_class.from_dict(d.get("config", {}))
return cls(config=config)

@abstractmethod
def run(self, microscope: FibsemMicroscope, stage: "FibsemMillingStage", asynch: bool = False, parent_ui = None) -> None:
Expand Down Expand Up @@ -81,7 +84,7 @@ def __post_init__(self):
if self.imaging.resolution is None:
self.imaging.resolution = [1536, 1024] # default resolution for imaging

def to_dict(self):
def to_dict(self) -> dict[str, Any]:
return {
"name": self.name,
"num": self.num,
Expand All @@ -93,7 +96,7 @@ def to_dict(self):
}

@classmethod
def from_dict(cls, data: dict):
def from_dict(cls, data: dict) -> "FibsemMillingStage":
strategy_config = data.get("strategy", {})
strategy_name = strategy_config.get("name", "Standard")
pattern_name = data["pattern"]["name"]
Expand Down
9 changes: 5 additions & 4 deletions fibsem/milling/core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import time
from typing import List, Tuple
from os import PathLike
from typing import List, Tuple, Optional, Union

from fibsem import config as fcfg
from fibsem.microscope import FibsemMicroscope
Expand All @@ -23,7 +24,7 @@
def setup_milling(
microscope: FibsemMicroscope,
milling_stage: FibsemMillingStage,
ref_image: FibsemImage = None,
ref_image: Optional[FibsemImage] = None,
):
"""Setup Microscope for FIB Milling.

Expand Down Expand Up @@ -246,7 +247,7 @@ def acquire_images_after_milling(
microscope: FibsemMicroscope,
milling_stage: FibsemMillingStage,
start_time: float,
path: str,
path: Optional[Union[str, PathLike]],
) -> Tuple[FibsemImage, FibsemImage]:
"""Acquire images after milling for reference.
Args:
Expand All @@ -265,7 +266,7 @@ def acquire_images_after_milling(

# set imaging parameters (filename, path, etc.)
if milling_stage.imaging.path is None:
milling_stage.imaging.path = path
milling_stage.imaging.path = str(path)
milling_stage.imaging.filename = f"ref_milling_{milling_stage.name.replace(' ', '-')}_finished_{str(start_time).replace('.', '_')}"

# from pprint import pprint
Expand Down
Loading