Skip to content
# === Cartesian Oval Lens for Optimal Fiber Coupling ===
#
# The Cartesian oval eliminates spherical aberration by enforcing a constant
# optical path length (OPL) for every ray from the point emitter to the
# collimated output plane.  For coupling into a single-mode fiber whose
# Gaussian mode has a flat wavefront at the fiber face $z_R >> d_cf$, the
# optimal design collimates the emitter's spherical wave into a plane wave.
#
# OPL condition:   $n_sil$ * sqrt$ρ² + z²$ - $n_out$ * z = $n_sil - n_out$ * h
#
# Closed-form:     z(ρ) = $n_out·h + n_sil·√$h² - κρ²$$ / $n_sil + n_out$
#                  κ = $n_sil + n_out$ / $n_sil - n_out$
#
# Valid aperture:  ρ ≤ ρ_max = h / √κ   (limited by total internal reflection)

ring_radii = np.linspace$0.0, sil_radius, num_rings$
$h_apex$ = sil_radius  # On-axis apex height (matches hemisphere)

$n_out$ = 1.0  # Exit medium $air/vacuum$
kappa = $n_sil + n_out$ / $n_sil - n_out$
rho_max = $h_apex$ / np.sqrt(kappa)

# Cartesian oval profile within the TIR-limited aperture
discriminant = $h_apex$**2 - kappa * ring_radii**2
in_aperture = discriminant >= 0
oval_heights = np.where$in_aperture,
    ($n_out$ * $h_apex$ + $n_sil$ * np.sqrt(np.maximum(discriminant, 0)))
    / ($n_sil$ + $n_out$),
    0.0,$

# Beyond ρ_max: linear taper to min_height at the SIL edge
$h_edge$ = $n_out$ * $h_apex$ / $n_sil + n_out$  # Height at ρ = ρ_max
taper = $h_edge$ - $h_edge - min_height$ * $ring_radii - rho_max$ / $sil_radius - rho_max$
init_params = np.where$in_aperture, oval_heights, np.maximum(taper, min_height)$

theta_c = np.degrees$np.arcsin($n_out$ / $n_sil$)$
print$f"Cartesian oval:  κ = {kappa:.1f},  ρ_max = {rho_max:.3f} μm,  $h_edge$ = {$h_edge$:.3f} μm"$
print$f"Critical angle   = {theta_c:.1f}°"$

# Build and visualize simulation
init_sim = get_simulation$init_params$

fig = plt.figure$tight_layout=True, figsize=(14, 5)$
gs = fig.add_gridspec(2, 3)
ax1 = fig.add_subplot$gs[:, 0]$
ax2 = fig.add_subplot$gs[0, 1]$
ax3 = fig.add_subplot$gs[1, 1]$
ax4 = fig.add_subplot$gs[:, 2]$

init_sim.plot_eps$z=$t_sio2$ + sil_radius / 2, ax=ax1, monitor_alpha=0$
init_sim.plot$y=0, ax=ax2, monitor_alpha=0$
init_sim.plot_eps$y=0, ax=ax3$

# Compare lens profiles
hemisphere = np.sqrt$np.maximum(sil_radius**2 - ring_radii**2, 0.0)$
ax4.plot$ring_radii, hemisphere, "k--", alpha=0.5, label="Hemisphere"$
ax4.plot$ring_radii, init_params, "b-", label="Cartesian oval"$
ax4.axvline$rho_max, color="gray", ls=":", alpha=0.5, label=f"$\\rho_{{max}}$ = {rho_max:.2f} μm"$
ax4.set_xlabel("Radial position ρ (μm)")
ax4.set_ylabel$"Height (μm)"$
ax4.set_title("SIL surface profile")
ax4.legend$fontsize=9$
plt.show()

Pasted image 20260324114237.png

Pasted image 20260324114247.png

Pasted image 20260324114200.pngPasted image 20260324114754.png

Run 2026-03-24

init_params = np.array([2.15      , 2.15      , 2.15      , 2.15      , 2.15      ,
       2.15      , 2.15      , 2.15      , 2.15      , 2.15      ,
       2.15      , 2.15      , 2.1313322 , 2.086171  , 2.0464349 ,
       2.041331  , 2.0427978 , 2.0436196 , 2.0728662 , 2.1186922 ,
       2.1452532 , 1.8408425 , 1.7036216 , 1.6433294 , 1.55631   ,
       1.4533588 , 1.3277657 , 1.1201562 , 0.78409135, 0.58579063,
       0.72580475, 0.6003881 , 0.6798209 , 0.6882752 , 0.64195395,
       0.69327646, 0.6895122 , 0.7104395 , 0.7380386 , 0.6748007 ,
       0.50348157, 0.31806147, 0.07350177, 0.05      , 0.21639279,
       0.6237649 , 0.66108406, 0.58662486, 0.49266502, 0.4052913 ,
       0.28797153, 0.13498408, 0.05      , 0.10974871, 0.1578338 ,
       0.15337527, 0.12088381, 0.08042433, 0.05902958, 0.1153025 ,
       0.6914359 , 0.65003127, 0.57438517, 0.52466124], dtype=float32)

d_cf = 2.15

Pasted image 20260324133304.png

Pasted image 20260324133313.png

Doing a position sweep again

Pasted image 20260324133938.png

Updated params

array([2.15      , 2.15      , 2.15      , 2.15      , 2.15      ,
       2.15      , 2.15      , 2.15      , 2.15      , 2.15      ,
       2.15      , 2.15      , 2.097241  , 2.0641246 , 2.0564694 ,
       2.0771244 , 2.0746922 , 2.0511847 , 2.0654542 , 2.1104598 ,
       2.0746374 , 1.6593317 , 1.61374   , 1.7730736 , 1.6936587 ,
       1.5123912 , 1.3318948 , 1.0490649 , 0.6102792 , 0.42934996,
       0.7591557 , 0.75087565, 0.9297884 , 0.8957742 , 0.8042245 ,
       0.8338097 , 0.7845124 , 0.5903704 , 0.40914237, 0.27681845,
       0.35202086, 0.37775597, 0.3396622 , 0.36298108, 0.18884102,
       0.27224046, 0.4062518 , 0.62768054, 0.7888396 , 0.61301583,
       0.348251  , 0.05      , 0.05      , 0.05      , 0.05      ,
       0.13144836, 0.15863298, 0.05      , 0.29891923, 0.5672724 ,
       1.0776697 , 0.6546207 , 0.2848655 , 0.14540468], dtype=float32)

Pasted image 20260324204615.png\

[!ERROR] I realized that I was clipping the height of the lens. I am starting over with different constraints.

array([2.6623397 , 2.6353512 , 2.5762963 , 2.5094512 , 2.4548113 ,
       2.417729  , 2.3731413 , 2.288861  , 2.3609178 , 2.4291139 ,
       2.4739783 , 2.3005333 , 2.4113936 , 2.5722444 , 2.66333   ,
       1.8571405 , 1.9002906 , 1.9994804 , 2.0997105 , 2.121043  ,
       1.992148  , 1.8380157 , 1.9888959 , 2.331807  , 2.436948  ,
       1.4891073 , 1.1165357 , 1.1380515 , 1.4260321 , 1.5764182 ,
       1.3656145 , 0.6193609 , 0.3750911 , 0.5133566 , 0.7549467 ,
       0.9513143 , 1.0200113 , 0.77997386, 0.3790514 , 0.458539  ,
       0.58106464, 0.54539514, 0.32169437, 0.09893914, 0.1696058 ,
       0.35921615, 0.39739528, 0.36803186, 0.4783354 , 0.62903315,
       0.6235232 , 0.22600067, 0.05      , 0.05      , 0.05      ,
       0.15269797, 0.24259725, 0.17233059, 0.05139003, 0.05      ,
       0.05      , 0.5704556 , 0.7359599 , 0.7268702 ], dtype=float32)

Pasted image 20260325081407.png

Pasted image 20260325081419.png

Optimizing again with no gold reflector

Optimization conditions - No backreflector - Azimuthal symmetry

First completed design inv_des_sil.ipynb

sil_with_supports (3).stl

params = np.array([2.3806891  2.446803   2.5148678  2.4354632  2.2483227  2.4718928
 2.620887   2.5574138  2.5727844  2.9399548  2.9790215  2.7284467
 2.5522861  2.8535383  2.8400538  2.3388877  2.2373493  2.3052921
 2.3545012  2.108885   2.3334634  1.9900931  1.6770164  1.8462439
 1.930206   2.1130807  3.1932764  4.028008   1.1316414  0.05
 0.05       0.05       0.72253263 0.67029333 0.5386423  0.5216792
 0.52670884 0.6419068  0.5375593  0.5078126  0.7120204  1.2719915
 0.8124067  0.05       0.05       0.15194799 0.7220474  0.3473141
 0.12052727 0.41661236 1.0029374  0.32026052 0.05       0.05
 0.1289356  0.36760283 0.0958149  0.05       0.0500295  0.730262
 0.05       0.05       0.05       0.05      ])

Optimizing again to make sure

With this one, I started with a dome and optimized from there, instead of optimizing from a Cartesian oval.

sil_with_supports (2).stl sil_lens_only (2).stlinv_des_sil 1.ipynb

Pasted image 20260329061716.png

Pasted image 20260329062608.png

Pasted image 20260329062748.png

Pasted image 20260329062820.png

Just a simple Cartesian oval

sil_with_supports (4).stlsil_lens_only (4).stl Pasted image 20260329063448.png

Pasted image 20260329063516.png

This design should still give a few million photons per second at 780 nm.