-
-
Notifications
You must be signed in to change notification settings - Fork 81
[Demo] Multi order diffraction #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
singer-yang
commented
Dec 31, 2025
- Add a tutorial demo to visualize multi-order diffraction
- Refactor diffractive surface functions
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on January 7. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a multi-order diffraction visualization demo and refactors diffractive surface functions for improved code organization and extensibility. The changes introduce a new Grating DOE class for simulating linear grating diffraction patterns and standardize the API across all diffractive optical elements.
Key changes:
- Adds a new
Gratingdiffractive optical element for linear grating patterns with configurable angle and phase gradient strength - Refactors diffractive surface methods by renaming
_phase_map0()tophase_func()and introducing quantization support via a newdiff_quantizeautograd function - Adds
fab_stepparameter across all DOE classes for better fabrication control
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| deeplens/optics/utils.py | Adds diff_quantize autograd function for differentiable quantization using Straight-Through Estimator; removes unused torch.nn import |
| deeplens/optics/diffractive_surface/diffractive.py | Refactors phase calculation methods (_phase_map0() → phase_func()), adds fab_step parameter, integrates quantization, simplifies visualization methods, and marks computed fields with parentheses in surf_dict() |
| deeplens/optics/diffractive_surface/grating.py | New Grating DOE class implementing linear phase gradient for multi-order diffraction with configurable angle (theta) and gradient strength (alpha) |
| deeplens/optics/diffractive_surface/zernike.py | Updates to use new phase_func() method, adds fab_step parameter, and refactors init_from_dict() for cleaner parameter handling |
| deeplens/optics/diffractive_surface/thinlens.py | Updates to use new phase_func() method, adds fab_step parameter, and refactors init_from_dict() |
| deeplens/optics/diffractive_surface/pixel2d.py | Updates to use new phase_func() method, adds fab_step parameter, and refactors init_from_dict() |
| deeplens/optics/diffractive_surface/fresnel.py | Updates to use new phase_func() method, adds fab_step parameter, and refactors init_from_dict() |
| deeplens/optics/diffractive_surface/binary2.py | Updates to use new phase_func() method, adds fab_step parameter, and refactors init_from_dict() |
| deeplens/optics/diffractive_surface/init.py | Adds Grating to module exports |
| deeplens/hybridlens.py | Adds support for loading Grating DOE from JSON configuration using "type" field instead of "param_model" |
| datasets/lenses/hybridlens/a489_grating.json | Configuration file for A489 lens with linear grating DOE for multi-order diffraction demonstrations |
| 8_multi_order_diffraction.py | Demo script visualizing multi-order diffraction PSF patterns from a hybrid lens with grating, showing both linear and log-scale plots for multiple wavelengths |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
deeplens/hybridlens.py
Outdated
| doe = Grating.init_from_dict(doe_dict) | ||
| else: | ||
| raise ValueError( | ||
| f"Unsupported DOE parameter model: {doe_dict['param_model']}" |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message references 'doe_dict["param_model"]' but the code now uses 'doe_dict["type"]' (line 77). The error message should be updated to reference the correct dictionary key for consistency.
| f"Unsupported DOE parameter model: {doe_dict['param_model']}" | |
| f"Unsupported DOE parameter model: {doe_param_model}" |
| fab_step=doe_dict.get("fab_step", 16), | ||
| ) | ||
|
|
||
| def get_phase_map(self, wvln=0.55): |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ThinLens.get_phase_map method has a default parameter value 'wvln=0.55', but the parent class DiffractiveSurface.get_phase_map now requires 'wvln' without a default. This creates an inconsistency in the API. Either remove the default from ThinLens to match the parent class signature, or add it back to the parent class if a default is desired.
| def get_phase_map(self, wvln=0.55): | |
| def get_phase_map(self, wvln): |
deeplens/optics/utils.py
Outdated
| @staticmethod | ||
| def forward(ctx, x, levels, interval=2 * torch.pi): | ||
| step = interval / levels | ||
| ctx.step = step |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable 'ctx.step' is saved in the forward pass but never used in the backward pass. Since the Straight-Through Estimator simply passes gradients through unchanged, this variable can be removed to avoid unnecessary state storage.
| ctx.step = step |
| self.theta = torch.tensor(theta) # angle from y-axis to grating vector | ||
| self.alpha = torch.tensor(alpha) # slope of the grating |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tensors for theta and alpha are created without specifying the device parameter, then the object is moved to device later with self.to(device). For consistency and to avoid potential device mismatch issues, these tensors should be created with the device parameter directly: torch.tensor(theta, device=device).
| self.theta = torch.tensor(theta) # angle from y-axis to grating vector | |
| self.alpha = torch.tensor(alpha) # slope of the grating | |
| self.theta = torch.tensor(theta, device=device) # angle from y-axis to grating vector | |
| self.alpha = torch.tensor(alpha, device=device) # slope of the grating |
| axes[0].axvline( | ||
| x=len(psf_center) // 2, | ||
| color="red", | ||
| linestyle="--", | ||
| alpha=0.5, | ||
| label="Center", | ||
| ) |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The axvline is created with a label="Center" parameter after the legend has already been created (line 50). This means the "Center" label will not appear in the legend. Either remove the label parameter from axvline (since it's self-explanatory as a visual guide), or move the legend() call to after the axvline if the label should be included in the legend.
| axes[1].axvline( | ||
| x=len(psf_center) // 2, | ||
| color="red", | ||
| linestyle="--", | ||
| alpha=0.5, | ||
| label="Center", | ||
| ) |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The axvline is created with a label="Center" parameter after the legend has already been created (line 71). This means the "Center" label will not appear in the legend. Either remove the label parameter from axvline (since it's self-explanatory as a visual guide), or move the legend() call to after the axvline if the label should be included in the legend.
| from .zernike import Zernike | ||
|
|
||
| __all__ = ["DOE", "Fresnel", "Pixel2D", "ThinLens", "Zernike", "Binary2"] | ||
| __all__ = ["DOE", "Fresnel", "Grating", "Pixel2D", "ThinLens", "Zernike", "Binary2"] |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name 'DOE' is exported by all but is not defined.
| __all__ = ["DOE", "Fresnel", "Grating", "Pixel2D", "ThinLens", "Zernike", "Binary2"] | |
| __all__ = ["DiffractiveSurface", "Fresnel", "Grating", "Pixel2D", "ThinLens", "Zernike", "Binary2"] |