Skip to content

Glass Expert #211

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

Merged
merged 12 commits into from
Jul 5, 2025
Merged

Glass Expert #211

merged 12 commits into from
Jul 5, 2025

Conversation

drpaprika
Copy link
Collaborator

@drpaprika drpaprika commented Jul 4, 2025

Hi @harrison, all,

It's been some time since my last contribution — it's nice to see that Optiland is continuously growing.

I've been working on an optimization feature called GlassExpert, which adds support for the optimization of categorical glass variables.
It explores a user-defined list of glasses and iteratively tests alternatives to find better combinations.
This is a powerful tool used by CodeV and Zemax as selling points, and I believe Optiland could benefit from this functionality as well.

The main challenge was to integrate discrete choices (glass materials) into the existing continuous optimization framework.
To do this, I implemented a greedy search strategy that cycles through all glass surfaces marked as variables,
evaluates options in (n_d, V_d) space, and keeps any substitution that reduces the objective function.

Key components:

  • GlassExpert: the main optimizer class, subclassing OptimizerGeneric
  • get_neighbour_glasses: finds the closest glasses in material space (n_d, V_d)
  • downsample_glass_map: uses K-Means to reduce the search pool while preserving diversity
  • plot_glass_map: for debugging and visualization of the current selection.

Please run Tutorial_7e_GlasseExpert.ipynb to get started.

The code is modular, and I've added comments and docstrings to explain each part.
It should integrate well with existing workflows and allow users to experiment with glass substitutions in a controlled way.

Looking forward to your feedback, and happy to adjust anything you find unclear or unpolished.

Thanks again for the great work on this project.

@drpaprika
Copy link
Collaborator Author

drpaprika commented Jul 4, 2025

Example

We start with a voluntary bad 6 lenses objective, all N-BK7 lenses:
image
image

For each lens, we define a list of glasses corresponding to the glasses in both Schott and Ohara catalogs that are transmissive in the wavelength band, either:

  • explicitely with glasses = ['N-BK7', 'S-BSM22', 'LF5G19', ...]
  • more broadly using glasses = material_utils.glasses_selection(0.4, 0.7, catalogs=["schott", "ohara"]).

The list of glasses can be different for each lens and is defined, like other variables, using:
problem.add_variable(lens, "material", surface_number=1, glass_selection=glasses)

We then run GlassExpert, which makes two optimization passes:

During the first pass, we perform for each glass variable a broad search over num_neighbours glasses in the entire glass catalogue. Example with 6 glasses:

image

During the second pass, we retreive for each glass variable the num_neighbours nearest materials in (n_d, V_d) space, and perform a focused local around the current glass choices. An example of local search around FK3:

image

At each step, for every candidate glass we:
- Substitute it into the design
- Run a continuous local optimization to evaluate performance
- If the objective improves, keep the substitution; otherwise, roll back.

Finally, a last optimization is performed over all continuous variables to polish the solution.

The merit function value during a GlassExpert run can look as follows (for 7 neighbours):
image
The error function jumps are normal and correspond to the optic being restored to its previous best state, or the evaluation of glasses far from the current glass. Also please note that the run duration scales with the number of lenses and the number of glass neighbours.

The resulting optic is as follows:
image
image

Copy link

codecov bot commented Jul 4, 2025

Codecov Report

Attention: Patch coverage is 81.70732% with 45 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
optiland/materials/material_utils.py 65.04% 43 Missing ⚠️
optiland/optimization/glass_expert.py 97.67% 2 Missing ⚠️

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #211   +/-   ##
=========================================
  Coverage          ?   95.52%           
=========================================
  Files             ?      162           
  Lines             ?     9013           
  Branches          ?        0           
=========================================
  Hits              ?     8610           
  Misses            ?      403           
  Partials          ?        0           
Files with missing lines Coverage Δ
optiland/materials/__init__.py 100.00% <100.00%> (ø)
optiland/optic/optic.py 97.95% <100.00%> (ø)
optiland/optic/optic_updater.py 95.78% <100.00%> (ø)
optiland/optimization/__init__.py 100.00% <100.00%> (ø)
optiland/optimization/optimization.py 96.88% <100.00%> (ø)
optiland/optimization/variable/__init__.py 100.00% <100.00%> (ø)
optiland/optimization/variable/material.py 100.00% <100.00%> (ø)
optiland/optimization/variable/variable.py 100.00% <100.00%> (ø)
optiland/visualization/visualization.py 95.72% <100.00%> (ø)
optiland/optimization/glass_expert.py 97.67% <97.67%> (ø)
... and 1 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@HarrisonKramer
Copy link
Owner

Wow, this is an incredible contribution! Really well thought out, clearly implemented, and significantly expands the optimization capabilities. The approach to discrete material optimization is well-structured, and it fits neatly into the existing optimization framework. The two-pass strategy, wide then local, is a very practical way to handle glass selection, and I like that it leaves room for user control.

The code is modular, readable, and the example notebook helps clarify how to use it. Everything looks good on my end and tests pass, so I’ll go ahead and merge. Thanks!

I'd also like to expand the documentation a bit, e.g., adding this to the learning guide, example gallery, and maybe some references through the readthedocs docs, but that's not urgent. I can help with that as a follow-up.

Looking forward to more like this!

Best,
Kramer

@HarrisonKramer HarrisonKramer merged commit 3fc87e1 into master Jul 5, 2025
12 checks passed
@HarrisonKramer
Copy link
Owner

Documentation for Glass Expert expanded in #213.

@HarrisonKramer HarrisonKramer deleted the glass_expert branch July 6, 2025 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants