Enhance aspheric surface management and optimize lens design#111
Merged
singer-yang merged 31 commits intodevfrom Feb 28, 2026
Merged
Enhance aspheric surface management and optimize lens design#111singer-yang merged 31 commits intodevfrom
singer-yang merged 31 commits intodevfrom
Conversation
Implement two new methods for progressive aspheric surface management during differentiable lens design optimization: - add_aspheric: converts Spheric to Aspheric with auto-selection of best candidate based on aperture stop proximity and air-glass interface heuristics (following classical design principles) - increase_aspheric_order: incrementally adds higher-order polynomial terms to existing Aspheric surfaces Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both are physically identical to air (n=1). Material.__init__ now maps legacy names to "air" at init time, keeping backward compatibility with existing lens JSON files. Removed redundant entries from materials_data.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 2nd-order aspheric coefficient (a2) competes with base curvature c and should not be optimised. Change ai list to [a4, a6, a8, ...] across Aspheric, AsphericNorm, and _increase_surface_order. Old JSON files with a2 are handled transparently via the use_ai2 backward-compat flag. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When surf_idx is not given, pick ONE best aspheric surface instead of increasing all. Selection ranks by lowest ai_degree, then largest semi-diameter (marginal ray height proxy), then highest Δn at interface. Follows Principle 5: one surface, one term at a time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New standalone script (9_autolens_aspheric.py) that starts from all-spherical surfaces, gradually expands aperture, and introduces aspheric surfaces and higher-order terms at stage boundaries. Uses add_aspheric and increase_aspheric_order for staged aspherization. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 2nd asphere auto-selection picked surface 0 (front element), which is impractical in camera lens design. Now _find_best_asphere_candidate excludes the first and last refractive surfaces from subsequent asphere selection, falling back to the full list only when no inner candidates remain. Updated design principles doc with the outermost-surface rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move aspheric surface management (add_aspheric, increase_aspheric_order and helpers) from GeoLensOptim and surface pruning/shape correction (prune_surf, correct_shape) from GeoLens into a dedicated GeoLensSurfOps mixin, keeping GeoLensOptim focused on loss functions and gradient-based optimisation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lization New GeoLensSeidel mixin computes per-surface third-order (Seidel) aberration coefficients via paraxial ray tracing and renders a Zemax-style grouped bar chart. Supports both Aspheric and AsphericNorm surfaces, and includes chromatic aberration (C_L, C_T). Co-Authored-By: Claude <noreply@anthropic.com>
Moved WVLN_d, WVLN_F, and WVLN_C constants from seidel.py to config.py to centralize optical configuration parameters. Updated imports in seidel.py to use the new location.
… failures Rename mixin files for consistent naming: - seidel.py → eval_seidel.py - tolerance.py → eval_tolerance.py - surf_ops.py → optim_ops.py - view_3d.py → vis3d.py Move match_materials() from geolens.py to optim_ops.py. Update all imports, docstrings, and documentation references. Fix three pre-existing test issues: - test_material_default: expect "air" (Material normalizes None to "air") - test_aspheric_higher_order: expect ai4*r^4 (ai list starts at a4, not a2) - test_read_zmx: use available ef35mm_f2.0.zmx fixture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ~55 print() statements with proper logging using module-level loggers across 12 files. Fix bare except: clause in io.py with specific exception types. Fix root logger configuration in optim.py to use module-level logger. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove redundant z and point_source tensor creation inside the if quater: block that duplicated lines already executed above. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…add __all__ Replace from .module import * with explicit named imports in 6 __init__.py files. Add __all__ to all package __init__.py files for explicit public API definition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…module" This reverts commit 588c11a.
Add random parameter sampling across three lens categories (camera spheric, camera aspheric, mobile) with SLURM GPU job and CPU dispatcher scripts for running large-scale automated lens design experiments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… for loading The EFL calculation was returning unreasonable values (~139mm instead of ~5mm) for cellphone lenses because: 1. io.py loaded "Aspheric" JSON data as AsphericNorm instead of Aspheric 2. The a2 (2nd-order) aspheric coefficient was silently discarded, losing ~84% of the paraxial curvature for lenses that rely on a2 Changes: - Aspheric: add ai2 parameter, include a2*r² in sag and derivative computation - io.py: use Aspheric.init_from_dict() for "Aspheric" type surfaces - Preserve ai2 through save/load round-trips and Zemax export (PARM 1) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove `logging` module import and `logger` instance. Convert the debug message regarding legacy format handling from `logger.debug` to a standard `print` statement using an f-string.
Replace sequential per-field-angle ray tracing with batched GPU computation. All field angles are packed into a single [F, spp, 3] tensor and traced in one call per wavelength (3 calls vs 96). Add parabolic interpolation for sub-grid defocus precision, boundary warnings, and NaN masking for fully-vignetted field angles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Vignetting: use uniform_fov=False for correct sensor-space mapping (was distorted for wide-angle lenses), reduce rays 16384→512 and grid 64→32 (~130x speedup), remove 0.5+0.5x scaling that compressed dynamic range, show full [0,1] colorbar - Distortion: skip near-zero FOV angles to avoid numerical instability in ray aiming, prepend FOV=0 with distortion=0 by definition, add min_delta floor in chief ray search to prevent degenerate range - Fix pre-existing bug in calc_chief_ray_infinite where hardcoded [1:] indexing failed when input had no zero-angle entry (use dynamic start_idx) - Add @torch.no_grad() to vignetting, distortion, and analysis_spot functions for memory efficiency - Use consistent calc_entrance_pupil() call in ray aiming Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Noise modeling does not belong in the optics simulation layer. Remove the noise parameter and Gaussian noise injection from analysis_rendering and its caller in analysis(). Sensor noise is handled separately by the Camera class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The distortion at FOV=0 was hardcoded to 0%, but the true limit can be non-zero when the sensor is defocused from the paraxial focal plane. Replace the split sampling (skip small FOV + prepend 0) with uniform sampling and compute the on-axis point at a tiny angle to get the correct limit value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove redundant enpd parameter (derived from foclen/fnum) - Rename flange → bfl (back focal length) to match standard optics terminology and existing self.bfl in geolens.py - Allow specifying either foclen or imgh (mutually exclusive), with the other derived via imgh = 2 * foclen * tan(fov/2) - Rename flange_min/flange_max → bfl_min/bfl_max in optim.py - Update all call sites (4 files) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove AsphericNorm and WAVE_RGB imports as they are not utilized in the module.
… radius
Aspheric._sag() computes ai_{2n} * r^{2n} with raw coordinates, so
gradients scale as O(r^{2n}). For camera lenses (r=20-25mm) this
produces gradient magnitudes of ~10^5, causing NaN within ~40 iterations.
Additionally, prune_surf oscillates surface semi-diameters between
evaluation steps (e.g. 5.3mm -> 24.9mm), causing ~500x gradient swings
that corrupt Adam's second-moment estimates and lead to 30x oversized
steps.
Scale each ai coefficient's lr by 1/max(r,1)^{2n} so the effective sag
perturbation per Adam step is constant (~lr_base mm) regardless of
surface size. The old per-order decay is removed since the r-scaling
already normalises across orders, matching AsphericNorm's constant-lr
behaviour.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
set_target_fov_fnum() and set_fov() overwrote self.rfov without recomputing self.eqfl, leaving the stale value from initial lens load. This caused layout titles to display wrong equivalent focal lengths (e.g. 1838mm instead of ~34mm). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the `decay` parameter from the entire aspheric optimization
chain (optimize, get_optimizer, get_optimizer_params) since lr
normalisation by 1/r^{2n} already balances all aspheric orders.
Also rename remaining `flange` references to `bfl` in configs and
scripts for consistency with the create_lens API change, and update
cellphone lens datasets with use_ai2 flag and Aperture type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AsphericNorm is no longer needed — Aspheric.get_optimizer_params now
applies per-order lr normalization (lr / r^{2n}), achieving the same
gradient stability. Batch results confirm 0 crashes with Aspheric alone.
- Delete aspheric_norm.py
- Remove imports and isinstance checks from optim, optim_ops, eval_seidel, io
- Remove TestAsphericNorm from test suite
- Update README, CLAUDE.md, and example scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document recent breaking changes: flange→bfl rename, enpd removal, foclen/imgh mutual exclusivity in create_lens, and decay parameter removal from the optimization API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add CLAUDE.md to .gitignore — it contains local project notes that should not be committed to the repository. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces several improvements and refactors across the lens design codebase, focusing on configuration consistency, code organization, and enhancements to lens data files. The most significant changes include standardizing parameter names in configuration and code, updating the surface types and features in lens JSON files, and improving import structure and module exports for better maintainability and clarity.
Configuration and Parameter Standardization
flangeparameter withbfl(back focal length) in both the YAML configuration file (configs/2_auto_lens_design.yml) and the codebase, ensuring consistent terminology and usage throughout the project. [1] [2]decayparameter from optimizer-related function calls and configuration, simplifying the optimizer setup in2_autolens_rms.pyand the YAML config. [1] [2] [3] [4] [5]Lens Data File Enhancements
"Stop"to"Aperture"in bothcellphone68deg.jsonandcellphone80deg.json, aligning with updated modeling conventions. [1] [2]"use_ai2": trueproperty to all"Aspheric"surfaces in both cellphone lens JSON files, indicating the use of a new aspheric modeling approach. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24]Code Organization and Module Exports
deeplens/optics/__init__.pyto explicitly import only the necessary classes and functions, and defined the__all__variable to clarify the public API of the optics module. [1] [2]deeplens/optics/geolens.pyto use new module names for Seidel evaluation, surface operations, tolerance analysis, and 3D visualization, reflecting codebase restructuring and improved modularity.Optical Wavelength Definitions
WVLN_d,WVLN_F,WVLN_C) todeeplens/optics/config.pyfor standard chromatic aberration reference, supporting more accurate optical calculations.Example and Analysis Improvements
0_hello_deeplens.pyto use the cellphone lens JSON and enabledfull_evalin the analysis for more comprehensive evaluation.