-
Notifications
You must be signed in to change notification settings - Fork 54
Description
Hi all! First, thanks for creating RayOpt, it looks like a wonderful library! I'm looking forward to playing around with it.
I had a few questions around how the optimizer works. Full disclosure, I'm a relative newbie at optics in general, so some of my confusion might be stemming from that :)
I grabbed the optimization code from the triplet example and tried to simplify it down to a long, air-spaced doublet. After playing around with parameters for a while, I got it to a point where it "works" (ok'ish spot diagram, etc)... but I'm not sure what all the various components actually do so it's hard to say how well it is working :) I'm happy to polish this up into an example optimizer demo for the notebook repo, complete with comments
My full script is here, and a few questions about how it works below:
variables.extend(PathVariable(s, (i, "curvature"), (-1/10, 1/10))
for i in (1, 2, 3, 4))How do the "bounds" work here? The docs for scipy.minimize suggest this is the extent the variables can reach, but in practice this seems to be something closer to step size during optimization? E.g. the ROC of a surface is not limited to [-0.1,0.1] when using the above parameters
def get(s):
s.update()
s.paraxial.focal_length_solve(500)
s.paraxial.propagate(start=-2)
s.paraxial.refocus()
s.paraxial.propagate(start=-1)
s.paraxial.resize()
s.paraxial.aberrations()In the get() function, what is the purpose of s.paraxial.propagate(start=-2) and s.paraxial.propagate(start=-1)? Skimming the source, it seems these might be the surfaces to start propogating from... but I don't understand why negative numbers are being used? I suspect I'm entirely incorrect about what these are for.
More generally, is there a high-level explanation of why the sequences is focal-length -> prop -> refocous -> prop -> resize -> aberr ?
operands = [
FuncOp(s, get),
#FuncOp(s, lambda s: s[-1].offset[2:], min=60),
#FuncOp(s, lambda s: s.edge_thickness()[1:], min=2),
#FuncOp(s, lambda s: np.diff(s.track), min=2),
FuncOp(s, lambda s: s.paraxial.transverse3[:, 5:].sum(0)/.5, min=-1, max=1),
FuncOp(s, lambda s: s.paraxial.transverse3[:, 3:5].sum(0)/1, min=-1, max=1),
FuncOp(s, lambda s: s.paraxial.transverse3[:, :3].sum(0)/.03, min=-1, max=1),
#FuncOp(s, lambda s: s.paraxial.transverse3[:, (0, 1, 2, 5, 6)].sum(0), weight=1),
GeomOp(s, rays=16, weight=1),
#PolyOp(s, weight=1),
]My understanding is that the operands define the function that we are trying to minimize. So the get() ensures we are hitting the correct focal length, the commented-out lines for offset and thickness can be used to control details about the surfaces, the s.paraxial.transverse3 conditions have something to do with minimizing aberrations and spot size, and not sure what the GeomOp does.
Is there an easy explanation what the s.paraxial.transverse3 conditions do? I tried to dig around in the code but wasn't able to figure it out. Similarly, I'm not sure what the commented out sum(0) condition does, or the PolyOp
Thanks for the help! As I said, happy to take this and help put together an "optimizer 101" example for other clueless newbies like myself :)