-
-
Notifications
You must be signed in to change notification settings - Fork 102
Description
Checklist
- I have searched the existing issues and discussions for a similar question or feature request.
- I have read the documentation and tried to find an answer there.
- I am using the latest version of Optiland.
- I have included all necessary context.
Thanks for taking the time to go through this — it really helps us help you!
Feature Request
Of course. Here is the final draft of the issue, written in English and incorporating all the details you've provided. You can copy and paste this directly into the GitHub issue submission form.
Feature Request
Is your feature request related to a problem? Please describe.
Currently, Optiland lacks support for surfaces with a gradient refractive index (GRIN), such as the gradient3 surface type available in Zemax. This limits the software's capability to design and simulate optical systems that utilize GRIN lenses. These lenses are crucial for correcting aberrations and reducing the number of optical elements in a system.
Furthermore, GRIN lenses are of significant importance in the fields of ophthalmology and optometry for simulating realistic eye models. Many established models, such as the Atchison and Liou-Brennan model eyes, rely on gradient refractive indices to accurately represent the human eye's crystalline lens. The absence of this feature in Optiland is a considerable limitation for researchers and engineers working on ophthalmic and vision science applications.
Describe the solution you'd like
I would like to request the addition of a new surface type that supports a gradient refractive index, similar to Zemax's gradient3. This would involve implementing a ray tracing algorithm capable of handling propagation through a medium with a continuously varying refractive index.
Describe alternatives you've considered
I have considered using a series of shells with uniform refractive indices to approximate a gradient index. However, this method is computationally intensive and would significantly slow down simulations, making it an impractical alternative for complex or iterative design processes.
Additional context
The importance of GRIN lenses is particularly evident in the simulation of human eye models, where they are used to accurately model the crystalline lens. For example, the Atchison (doi:10.1016/j.visres.2006.01.004) and Liou-Brennan model eyes (doi: 10.1364/josaa.14.001684) both utilize gradient-index lenses, which is a key reason for their anatomical and optical accuracy. Adding this functionality would make Optiland a much more powerful tool for ophthalmic and vision science research.
To assist with implementation, I am providing pseudocode for a ray tracing algorithm within a gradient medium. This algorithm uses the Runge-Kutta (RK4) method to solve the ray equation in a medium with a refractive index profile defined by radial and axial coefficients.
Here is the pseudocode:
// -------------------------------------------------------------------------
// Function: F_derivative(z, v, params)
// Purpose: Calculate the state derivative vector F = dv/dz at a given point (z, v)
// Inputs:
// z: current z-coordinate (float)
// v: current state vector [x, y, Tx, Ty, OP] (5D array)
// params: coefficients of the Gradient3 medium (struct or object)
// Output:
// Derivative vector F (5D array), or an error flag (e.g., null) if total internal reflection occurs
// -------------------------------------------------------------------------
FUNCTION F_derivative(z, v, params):
// 1. Unpack the state vector
x = v[0], y = v[1], Tx = v[2], Ty = v[3]
// 2. Efficiently calculate n and its partial derivatives
r_sq = x*x + y*y
r_sq_2 = r_sq * r_sq
r_sq_3 = r_sq_2 * r_sq
n = params.n0 + params.nr2*r_sq + params.nr4*r_sq_2 + params.nr6*r_sq_3 +
params.nz1*z + params.nz2*z*z + params.nz3*z*z*z
g_r = 2*params.nr2 + 4*params.nr4*r_sq + 6*params.nr6*r_sq_2
dn_dx = x * g_r
dn_dy = y * g_r
// 3. Calculate the Hamiltonian H and check for validity
n_sq = n*n
T_sq_transverse = Tx*Tx + Ty*Ty
radicand = n_sq - T_sq_transverse // Value inside the square root
IF radicand <= 0 THEN
// Total internal reflection or ray is perpendicular to the z-axis, cannot continue tracing with z as the step
RETURN null // Return an error flag
END IF
H = -sqrt(radicand)
// 4. Calculate and return the derivative vector F
inv_H = 1.0 / H // Pre-calculate the inverse to avoid multiple divisions
F = [
-Tx * inv_H,
-Ty * inv_H,
-n * dn_dx * inv_H,
-n * dn_dy * inv_H,
-n_sq * inv_H
]
RETURN F
END FUNCTION
// -------------------------------------------------------------------------
// Main procedure: RayTrace_Gradient3_RK4
// -------------------------------------------------------------------------
PROCEDURE RayTrace_Gradient3_RK4:
// 1. Initialization
// Define medium parameters
gradient_params = {n0, nr2, nr4, nr6, nz1, nz2, nz3}
// Define initial ray state
z_start = 0.0
z_end = 10.0
step_size = 0.1
initial_pos = [x0, y0]
initial_dir = [dir_x0, dir_y0, dir_z0] // Unit direction vector
// Calculate the initial state vector based on initial conditions
n_start = calculate_n(z_start, initial_pos, gradient_params) // Requires a helper function
Tx0 = n_start * initial_dir[0]
Ty0 = n_start * initial_dir[1]
z = z_start
v = [initial_pos[0], initial_pos[1], Tx0, Ty0, 0.0] // [x, y, Tx, Ty, OP]
// Store the trajectory
ray_path = []
ADD {z: z, state: v} TO ray_path
// 2. RK4 main loop
WHILE z < z_end:
// Ensure the last step ends exactly at z_end
current_step = min(step_size, z_end - z)
// Calculate K1
K1 = F_derivative(z, v, gradient_params)
IF K1 IS null THEN BREAK // Error handling
// Calculate K2
v_temp2 = v + (current_step/2.0) * K1
K2 = F_derivative(z + current_step/2.0, v_temp2, gradient_params)
IF K2 IS null THEN BREAK // Error handling
// Calculate K3
v_temp3 = v + (current_step/2.0) * K2
K3 = F_derivative(z + current_step/2.0, v_temp3, gradient_params)
IF K3 IS null THEN BREAK // Error handling
// Calculate K4
v_temp4 = v + current_step * K3
K4 = F_derivative(z + current_step, v_temp4, gradient_params)
IF K4 IS null THEN BREAK // Error handling
// 3. Update the state vector and z-coordinate
v = v + (current_step / 6.0) * (K1 + 2*K2 + 2*K3 + K4)
z = z + current_step
ADD {z: z, state: v} TO ray_path
END WHILE
// 4. Output the results
IF z >= z_end THEN
PRINT "Ray tracing completed successfully."
PRINT "Final state at z = ", z, ":"
PRINT " Position (x, y) = (", v[0], ", ", v[1], ")"
PRINT " Direction (Tx, Ty) = (", v[2], ", ", v[3], ")"
PRINT " Optical Path (OP) = ", v[4]
ELSE
PRINT "Ray tracing terminated early due to an error (e.g., total internal reflection) at z = ", z
END IF
END PROCEDURE