From 7df18324b12ab4723ea5a82171d0cb07be05fe9f Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 29 Jan 2026 17:39:47 +0000 Subject: [PATCH] feat(extractors): add annular aperture support to trajectory extractor --- .../extractors/extract_zernike_trajectory.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/optimization_engine/extractors/extract_zernike_trajectory.py b/optimization_engine/extractors/extract_zernike_trajectory.py index 5dffa24f..d2e2e4b2 100644 --- a/optimization_engine/extractors/extract_zernike_trajectory.py +++ b/optimization_engine/extractors/extract_zernike_trajectory.py @@ -249,6 +249,7 @@ class ZernikeTrajectoryExtractor: reference_angle: float = 20.0, unit: str = 'mm', n_modes: int = DEFAULT_N_MODES, + inner_radius: Optional[float] = None, # Annular aperture inner radius (mm) ): self.op2_path = Path(op2_file) self.focal_length = focal_length @@ -256,6 +257,7 @@ class ZernikeTrajectoryExtractor: self.unit = unit self.nm_scale = UNIT_TO_NM.get(unit, 1e6) # Default mm -> nm self.n_modes = n_modes + self.inner_radius = inner_radius # For annular aperture # Find geometry file if bdf_file: @@ -338,6 +340,18 @@ class ZernikeTrajectoryExtractor: dy = disp[:, 1] dz = disp[:, 2] + # Apply annular aperture mask if inner_radius specified + if self.inner_radius is not None: + r = np.sqrt(x**2 + y**2) + annular_mask = r >= self.inner_radius + x = x[annular_mask] + y = y[annular_mask] + z = z[annular_mask] + dx = dx[annular_mask] + dy = dy[annular_mask] + dz = dz[annular_mask] + node_ids = node_ids[annular_mask] + # Compute WFE (surface error * 2 for reflective surface) # For now, use Z-displacement (can add OPD correction if focal_length provided) surface_error = dz # In original units (mm) @@ -542,6 +556,7 @@ def extract_zernike_trajectory( focal_length: Optional[float] = None, reference_angle: float = 20.0, unit: str = 'mm', + inner_radius: Optional[float] = None, ) -> Dict[str, Any]: """ Extract Zernike trajectory metrics from OP2 file. @@ -556,6 +571,7 @@ def extract_zernike_trajectory( focal_length: Parabola focal length for OPD correction (mm) reference_angle: Reference angle (default 20°) unit: Displacement unit in OP2 ('mm', 'm', 'um', 'nm') + inner_radius: Inner radius for annular aperture (mm), excludes central hole Returns: Dict with trajectory analysis results including: @@ -572,6 +588,7 @@ def extract_zernike_trajectory( focal_length=focal_length, reference_angle=reference_angle, unit=unit, + inner_radius=inner_radius, ) return extractor.extract_trajectory(subcases=subcases, angles=angles)