bryancole / raypier_optics

A raytracing toolkit for optical design
Other
58 stars 8 forks source link

Sequential raytracing #3

Closed Jonas231 closed 2 years ago

Jonas231 commented 2 years ago

Hi Bryan, from your perspective what would be necessary in order to (re)implement sequential raytracing in raypier? Should this mode include more efficient raytracing and aiming without the need to check which is the next surface hit or not hit? Some sort of extrapolation of the important faces for optimization? Regards, Jonas

bryancole commented 2 years ago

If you already know the order in which Faces will be encountered, then what you want is a function with signature like:

def trace_sequential(input_rays : RayCollection, face_list: list) -> list-of-ray_collections

Writing such a function should not be too hard. For each Face, compute the next generation of rays using the same method as for the non-sequential trace. You just skip the step where you find the intersection of each ray for all faces and then choose the nearest.

This would only be worth it if you're interested in models with lots of faces and/or many generations of rays.

Assuming you have this non-sequential trace function, the next problem is then to find a way to make is easy to determine the order of faces. E.g. do a "guide-trace" first using non-sequential method with a small number of input rays. Then examine the results to deduce the Face sequence to pass to trace_sequential with a larger set of input rays.

In summary, this would be quite easy to implement but I've never had much motivation to do so myself, since non-sequential has always been fast enough and is much for convenient to use.

Bryan

On Thu, Nov 11, 2021 at 8:47 PM Jonas231 @.***> wrote:

Hi Bryan, from your perspective what would be necessary in order to (re)implement sequential raytracing in raypier? Should this mode include more efficient raytracing and aiming without the need to check which is the next surface hit or not hit? Some sort of extrapolation of the important faces for optimization? Regards, Jonas

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/bryancole/raypier_optics/issues/3, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABYLNQURDHBURFJD6DLPJTULQTWJANCNFSM5H3KHFJQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

bryancole commented 2 years ago

Thinking about this a bit more, the starting point is to make a new sequential version of raypier.core.tracer.trace_rays()

This function currently calls raypier.core.ctracer.trace_segment() in a loop, passing in a list of all FaceLists each time to do the trace non-sequentially. A crude method to make this almost-sequential is to iterate over your FaceList-list one face at a time (i.e. in a for-loop instead of within the while-loop), and pass only one FaceList at a time. However, since each FaceList instance can contain multiple faces (with a common translation/rotation) this isn't properly sequential.

To make a true sequential trace, we want a new method on the raypier.core.ctracer.FaceList object which performs an intersection on only one of its faces. I.e. have a c-method list like::

FaceList.intersect_one_face_c(ray_t *ray, vector_t ray_end, int face_idx)

where face_idx selects only one face within the FaceList for tracing. The .intersect_c() method is quite short and easy to modify in this way.

New sequential versions of raypier.core.ctracer.trace_segment_c() and raypier.core.ctracer.trace_gausslet_c() would also be required. The input list of FaceLists would be replaced by a single FaceList object and a face_idx to identify which face in the FaceList should be traced. Once the intersections have been found, the calculation of the child-rays is identical. It's a shame to duplicate this part of the code but I don't see a neat way of factoring out the intersection from the child-calculation that doesn't add more indirection.

bryancole commented 2 years ago

OK, I've just pushed a changeset to add a function "raypier.core.tracer.trace_ray_sequence()". This is totally untested but the c-components at least build without error. Hopefully this changeset should give you the idea. If I get time at the weekend I'll test it properly.

bryancole commented 2 years ago

This plan is taking shape. Having got a function for sequential tracing, I'm going to add a Bool trait "sequential" to all RaySource objects. RaySources will also gain a private _face_sequence list-object (empty by default). If "sequential" is set True and the _face_sequence is present, then a sequential trace is performed. If _face_sequence is empty, a regular non-sequential trace will be performed and the results use to compute the best face-sequence (and then set the _face_sequence trait accordingly). Thus, for sequential tracing, the first trace will always be non-sequential, then subsequent ones will be sequential, until either sequential is turned off, or the user calls ray_source.clear_face_sequence() to delete the current sequence. It'll be interesting to see what the performance benefits of sequential tracing are.

bryancole commented 2 years ago

OK, implementation is pushed to github and codebase. Sources now have a "sequential" check-box in the GUI. Check this and a non-sequential trace is performed to create the sequence for future traces. Trigger subsequent traces by typing "self.trace_all()" in the terminal window (I really ought to make a button for this). For a 4-face test (the examples/general_lens_example.py script), there was a modest speed-up of maybe 30%. Obviously bigger models are going to benefit more.

bryancole commented 2 years ago

If you can confirm this works for you, I'll close this issue.

Jonas231 commented 2 years ago

Hi Bryan, amazing how fast this can go. Thank you. I will try it out. I had this sequential raytracing in mind for optimizing some easy system (let's say a single lens) with scipy.optimize (like you proposed). Maybe we can define some operand for this (say I want to minimize some aberration like spherical. I would have to do a sequential raytrace, get information on the aberration, set radii and refractive index to variable and minimize the error function. Later on when the structure how to define these operands using your core.raytrace routines is clear more operands could be added by anyone. Best regards, Jonas

Jonas231 commented 2 years ago

It works for me now.

bryancole commented 2 years ago

Great. I just made one further small change: I've added a new uint32 member "ray_ident" to the ray_t data type. This is simply an arbitrary integer which each child ray inherits from its parent. I think this will be useful for situations where one needs to identify groups of rays within a traced RayCollection. I was thinking about lens optimisation problems: all the present Ray-Sources are either point-sources or collimated ray sources (I don't usually need to model imaging systems myself). I you are interested in optimising imaging performance, you probably want to minimise aberrations at multiple points over the image plane. To do this, you want a Ray Source which can generate groups of rays launched from multiple points on a grid in the image-plane. Lets call this a "PointGridSource". Although one could in theory create a grip of multiple point-RaySources, tracing one large RaySource is much more efficient than having multiple ones. After tracing the singe PointGridSource, you would need to calculate the point-spread for each sub-group of rays. The ray_ident values provide an efficient means of identifying which sub-group any one ray belongs to. The ray_ident values are assigned by the RaySource. You could also relate each end-ray by linking back to each parent ray via the parent_ray_idx member but this will be much less efficient. If you're doing the tracing as part of an optimisation routine, you want to go as fast as possible. At present, the ray_ident values will be zero as none of the sources set this value (except HexagonalRayFieldSource, which I used as a test; this sets the ray_ident to be an arange).

I'd be interested to see what you come up with for lens optimisation (I don't know much about lens design); If you want to contribute an optimisation example that would be great.

Jonas231 commented 2 years ago

I will certainly try to create an example here.