# How best to structure a physics (wave optics) simulator?

**URL:** <https://discourse.julialang.org/t/how-best-to-structure-a-physics-wave-optics-simulator/77952>\
**Category:** General Usage\
**Tags:** question\
**Created:** [March 16, 2022, 7:31am UTC](https://discourse.julialang.org/t/how-best-to-structure-a-physics-wave-optics-simulator/77952 "2022-03-16T07:31:55Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Yly](https://avatars.discourse-cdn.com/v4/letter/y/b9e5f3/32.png) [@Yly](https://discourse.julialang.org/u/Yly)\
**Post date:** [March 16, 2022, 7:31am UTC](https://discourse.julialang.org/t/how-best-to-structure-a-physics-wave-optics-simulator/77952/1 "2022-03-16T07:31:55Z")

</div>

I am a physicist working on a project involving wave optics, which I would like to use Julia for. Being relatively new to this language (and not a programmer by trade), I’m not sure of the best way to structure the problem in Julia, and I’m seeking advice on good practices, with an eye to performance and flexibility. In particular, I’m wondering if I can leverage Julia’s type system in an advantageous way.

The basic idea of what I want to do is this (in optics lingo, I’m modeling a 2f imaging system with various non-ideal features). To zeroth order, I have a function `propagator` that takes as input an array `fieldIn` and returns a modified Fourier transform `fieldOut`, which models the effect of passing light through an ideal lens. There are then various non-ideal features that one might like to incorporate: defocusing, aberrations, shot noise, camera saturation, etc. However, I might not want to have all these features present every time I run this function, either because I haven’t implemented them all yet, or because some are not relevant to a particular setup. Note also that some of these features are largely decoupled from others (e.g. camera saturation is applied after all the other operations), while others (such as aberrations) require modifications to the zeroth order `propagator` function.

If I were coding this in something like MATLAB, I’d either just duplicate the function `propagator` for every set of parameters, getting something like `propagator_aberration`, or simply keep adding optional parameters to the original function `propagator`. Now already Julia gives me a better option by letting me create a type for each non-ideal feature and dispatch on which features are supplied to a function.

I can see three ways to structure the problem from here:

1. Leave `propagator` as a function which dispatches on the features supplied to it.
2. Make an overarching type `opticalSystem` that includes all the features for a given setup. Supply this type to `propagator` instead of the individual features.
3. Make `propagator` itself a callable struct, with all the relevant features for a given setup as attributes.

My question is basically whether any of these is preferred, or perhaps whether there is an entirely different way that is better. It seems to me that the first one provides the greatest flexibility to add new methods, e.g. as I’m developing and implementing new features. However, it has a potential downside of being cumbersome when passing lots of different arguments for complicated setups.

What are good design principles for such a problem?

---

<div class="post-metadata">

**Author:** ![klafyvel](https://avatars.discourse-cdn.com/v4/letter/k/a587f6/32.png) [@klafyvel](https://discourse.julialang.org/u/klafyvel)\
**Post date:** [March 16, 2022, 10:12am UTC](https://discourse.julialang.org/t/how-best-to-structure-a-physics-wave-optics-simulator/77952/2 "2022-03-16T10:12:34Z")

</div>

Hi ! When I was a master student, I wrote a small library to deal with aberrations, and I implemented something that looked like option 2 (see [Example of using Wavefronts.jl · Wavefronts.jl documentation](https://klafyvel.github.io/Wavefronts.jl/stable/example/#Making-interference-patterns-1)). I liked this approach because it lets you build a system and separates the specification of the system from the physical investigation.

You also might want to have a look at these :

> **[GitHub - microsoft/OpticSim.jl: Optical Simulation software](https://github.com/microsoft/OpticSim.jl)**
>
> Optical Simulation software. Contribute to microsoft/OpticSim.jl development by creating an account on GitHub.

> **[GitHub - JuliaPhysics/PhysicalOptics.jl: A package for simulation of physical...](https://github.com/JuliaPhysics/PhysicalOptics.jl)**
>
> A package for simulation of physical optics. Physical optics is more general than ray optics but not as general as full electrodynamics. - GitHub - JuliaPhysics/PhysicalOptics.jl: A package for sim...

---

<div class="post-metadata">

**Author:** ![sharedskies](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sharedskies/32/26894_2.png) [@sharedskies](https://discourse.julialang.org/u/sharedskies)\
**Post date:** [April 18, 2023, 10:25pm UTC](https://discourse.julialang.org/t/how-best-to-structure-a-physics-wave-optics-simulator/77952/3 "2023-04-18T22:25:20Z")

</div>

There is an implication of order – first surface, second, and so on – which would be an issue in a comprehensive simulator that allows for scattering and back reflection. Each element of the system will generate a wavefront that then propagates and generates new ones. For a single surface, the simplest approach of a propagator function accepting an input wavefront, a specification of the surface, and returning an output wavefront(s) would be a building block without the constraint of knowing all the surfaces beforehand. [OpticSim](https://github.com/brianguenter/OpticSim.jl/tree/main/src/Optical) is a ray-tracing code, perhaps no longer being maintained. Another interesting approach is [PyOptica](https://pyoptica.gitlab.io/pyoptica-blog/) which has an impressive roadmap but is also not actively developed. If polarization is included the propagator will need electromagnetics too, and will depend on surface materials. An example is the behavior of blazed gratings where the shape of the grating groove and its conductivity presents a challenge to predict on first principles.
