-
Notifications
You must be signed in to change notification settings - Fork 9
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
Setting spherical object and image plane as optimisation target #29
Comments
Hi Kevin, Thanks for the question! You can define field points on a curved object surface by setting the object radius of curvature via the from optiland import optic
lens = optic.Optic()
lens.add_surface(index=0, thickness=100, radius=50.0)
lens.add_surface(index=1, thickness=7, radius=40.0, is_stop=True, material='N-SF2',
surface_type='even_asphere', coefficients=[0, 0, 0])
lens.add_surface(index=2, thickness=30.0, radius=-40.0)
lens.add_surface(index=3, radius=-50.0)
lens.set_aperture(aperture_type='objectNA', value=0.1)
lens.set_field_type(field_type='object_height')
lens.add_field(y=0)
lens.add_field(y=25)
lens.add_wavelength(value=0.55, is_primary=True)
lens.draw(num_rays=10) This results in the following image: Optimization can then be performed as follows: from optiland import optimization
problem = optimization.OptimizationProblem()
for field in lens.fields.get_field_coords():
input_data = {'optic': lens, 'surface_number': -1, 'Hx': field[0], 'Hy': field[1],
'num_rays': 5, 'wavelength': 0.55, 'distribution': 'hexapolar'}
problem.add_operand(operand_type='rms_spot_size', target=0, weight=1, input_data=input_data)
problem.add_variable(lens, 'radius', surface_number=1) # first surface
problem.add_variable(lens, 'radius', surface_number=2) # second surface
problem.add_variable(lens, 'radius', surface_number=3) # image surface
problem.add_variable(lens, 'asphere_coeff', surface_number=1, coeff_number=0)
problem.add_variable(lens, 'asphere_coeff', surface_number=1, coeff_number=1)
problem.add_variable(lens, 'asphere_coeff', surface_number=1, coeff_number=2)
problem.add_variable(lens, 'thickness', surface_number=2) # distance to image plane
optimizer = optimization.OptimizerGeneric(problem)
res = optimizer.optimize(tol=1e-9) And the resulting lens diagram: I recognize your remark about optimizing the RMS spot size on a curved surface. As it stands, the RMS spot size calculation (in If you have another operand that you find useful, consider adding it and submitting a pull request. I'd be happy to have it added to the operand list. I could also put something together if you have an idea of the algorithm. New operands are quite easy to add to the package. Regards, Edit: typo, change object "plane" to "surface" |
Hi Kramer,
Thanks so much for your response, very helpful. Regarding the spot size
measurement on a tilted surface, in my case the RoC is relatively large so
the error should be small for spots close to the diffraction limit, but I
can look I to defining an RMS metric that takes this into account early
January.
Best regards,
Kevin
…On Sat, 28 Dec 2024 at 17:39, Kramer ***@***.***> wrote:
Hi Kevin,
Thanks for the question!
You can define field points on a curved object plane by setting the object
radius of curvature via the radius parameter when you add the first
surface. The same can be done on the image surface. As an example:
from optiland import optic
lens = optic.Optic()
lens.add_surface(index=0, thickness=100, radius=50.0)lens.add_surface(index=1, thickness=7, radius=40.0, is_stop=True, material='N-SF2',
surface_type='even_asphere', coefficients=[0, 0, 0])lens.add_surface(index=2, thickness=30.0, radius=-40.0)lens.add_surface(index=3, radius=-50.0)
lens.set_aperture(aperture_type='objectNA', value=0.1)
lens.set_field_type(field_type='object_height')lens.add_field(y=0)lens.add_field(y=25)
lens.add_wavelength(value=0.55, is_primary=True)
lens.draw(num_rays=10)
This results in the following image:
image.png (view on web)
<https://github.com/user-attachments/assets/777aa285-cf18-43a5-b00a-bb0a499ca498>
Optimization can then be performed as follows:
from optiland import optimization
problem = optimization.OptimizationProblem()
for field in lens.fields.get_field_coords():
input_data = {'optic': lens, 'surface_number': -1, 'Hx': field[0], 'Hy': field[1],
'num_rays': 5, 'wavelength': 0.55, 'distribution': 'hexapolar'}
problem.add_operand(operand_type='rms_spot_size', target=0, weight=1, input_data=input_data)
problem.add_variable(lens, 'radius', surface_number=1) # first surfaceproblem.add_variable(lens, 'radius', surface_number=2) # second surfaceproblem.add_variable(lens, 'radius', surface_number=3) # image surfaceproblem.add_variable(lens, 'asphere_coeff', surface_number=1, coeff_number=0)problem.add_variable(lens, 'asphere_coeff', surface_number=1, coeff_number=1)problem.add_variable(lens, 'asphere_coeff', surface_number=1, coeff_number=2)problem.add_variable(lens, 'thickness', surface_number=2) # distance to image plane
optimizer = optimization.OptimizerGeneric(problem)
res = optimizer.optimize(tol=1e-9)
And the resulting lens diagram:
image.png (view on web)
<https://github.com/user-attachments/assets/b68e0c80-3dda-45b4-8cf3-368172f2c7e9>
I recognize your remark about optimizing the RMS spot size on a curved
surface. As it stands, the RMS spot size calculation (in
optiland.optimization.operand.ray) only takes into account the (x, y)
points of the ray intersection. This would ignore the projection onto a
non-planar or tilted surface, so you may want to create a new optimization
operand that takes into account the (x, y, z) points instead. One of the
tutorials describes how to set up user-defined operands
<https://github.com/HarrisonKramer/optiland/blob/master/docs/examples/Tutorial_5d_User_Defined_Optimization.ipynb>
.
If you have another operand that you find useful, consider adding it and
submitting a pull request. I'd be happy to have it added to the operand
list. I could also put something together if you have an idea of the
algorithm. New operands are quite easy to add to the package.
Regards,
Kramer
—
Reply to this email directly, view it on GitHub
<#29 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BI6IU2DZD2PSJVFECZFEBLL2H3H3TAVCNFSM6AAAAABUJJOFJKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKNRUGM3TMMZVGI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Hi Kramer, one question that remains is how I might define a ray bundle that is launched from the curved object surface but in a direction perpendicular to the surface - I'm sure this can be done using the ray class and ray generator and defining the direction cosines. However, it's not quite clear to me how one can trace a custom ray bundle through the system - is it possible to do this and then optimise on e.g. three custom ray bundles for three positions on the curved object surface, emitting into a cone defined by object NA but at a user-defined central angle? Basically, I want to simulate imaging emission from a fibre tip, which is fixed such that the emitting tip moves along a curved trajectory, and emission is along the axis of the fibre. Thanks again! |
Hi Kevin, You can trace any arbitrary ray bundle through the optical surface. All you need to do is manually define the rays (positions, direction cosines, relative intensities, wavelengths), create an instance of Here is a rough (untested) starting point for you. You'll have to fill in some of the logic. import numpy as np
from optiland.rays import RealRays
from optiland.optimization.operand import operand_registry
from optiland import optimization
def surface_normal(surf, x, y):
"""
Compute the surface normal at a given point (x, y) on the surface.
Workaround helper function (not most efficient way to do this...)
"""
z = surf.geometry.sag(x, y)
i = np.ones_like(x) # these values are not used, so we can set them to anything
rays_tmp = RealRays(x, y, z, i, i, i, i, i)
nx, ny, nz = surf.geometry.surface_normal(rays_tmp)
return -nx, -ny, -nz # return the normal pointing to the right
def generate_rays(nx, ny, nz, theta):
"""
Define a cone of rays centered around the normal vector
and into a cone with half-angle theta.
"""
# insert logic here
# All components are 1D numpy arrays
return RealRays(x, y, z, L, M, N, intensity, wavelength)
def new_metric(lens, x, y, theta):
"""
Compute the metric at a given point (x, y) on the surface.
"""
nx, ny, nz = surface_normal(surf, x, y)
rays_in = generate_rays(nx, ny, nz, theta)
# trace
rays_out = lens.surface_group.trace(rays_in)
# get x, y, z on image plane
x = rays_out.x
y = rays_out.y
z = rays_out.z
# insert logic here e.g., compute the RMS spot size
return metric # must return a scalar
operand_registry.register('my_new_metric', new_metric, overwrite=True)
problem = optimization.OptimizationProblem()
input_data = {'lens': lens, 'x': 0, 'y': 0, 'theta': 5.0}
problem.add_operand(operand_type='my_new_metric', target=0, weight=1, input_data=input_data)
# add one operand for each of your (x, y) points
# add variables, or more operands, etc.
# run optimizer
optimizer = optimization.OptimizerGeneric(problem)
res = optimizer.optimize(tol=1e-6) For reference, I also perform raytracing with custom rays in this example notebook. Hope that helps. Regards, |
Hi,
Another that question that popped up in my so far very enjoyable exploring of Optiland is how I might go about setting up a spherical object surface with rays launched into a object-side NA, and then having the lens system optimised such that the target image surface is also spherical, with a given RoC. Essentially three questions:
Appreciate any inputs here!
Thanks,
Kevin
The text was updated successfully, but these errors were encountered: