opticspy / lightpipes

LightPipes for Python, "Pure Python version"
https://opticspy.github.io/lightpipes/
BSD 3-Clause "New" or "Revised" License
227 stars 52 forks source link

How to achieve the Zernike polynomials combination with different j_terms? #48

Open ltang320 opened 3 years ago

ltang320 commented 3 years ago

I have a series of Zernike polynomials coefficients (j_terms form 1 ~36). However, I don't know how to achieve their combination by LightPipes.

Field2 = lp.Begin(10*lp.um,lamda,410)
for Noll in range(1, 36):
        (nz, mz) = lp.noll_to_zern(Noll)
        e_m = 2 if mz == 0 else 1
        A = 1
        #A = math.sqrt((2 * nz + 2) / e_m / math.pi)
        #print(e_m)
        Field2 = lp.Zernike(nz, mz, 10*lp.um, zernike_poly[Noll] * A, Field2)
phase2 = lp.PhaseUnwrap(phase2)

Screenshot 2020-11-27 at 18 11 33

I achieve it like this. But the phase is not as I imagined. It's very strange. Do you have any suggestions that how I can achieve this goal correctly?

(ldoyle edited code formatting)

ldoyle commented 3 years ago

Dear Vicky,

I think you just got the units wrong. In LightPipes the default units for Zernike polynomials is optical path length, i.e. [m]. This follows from the definition of the Zernike polynomials as a phase factor exp(1j*2*pi/lambda*Z). You have two options to solve this:

  1. scale the amplitude as desired, e.g. A=1*lamda for a 1 lambda = 2 Pi phase shift
  2. in the most recent LightPipes, you can specify alternative units in the Zernike function. Then, e.g. A=1 and units='lam' will also create a 2 Pi phase shift. Check my example below.
import numpy as np
import matplotlib.pyplot as plt

import LightPipes as lp

lamda = 800*lp.nm

zernike_poly = {i:1 for i in range(1,36)} #amplitude 1 for all indices

Field2 = lp.Begin(10*lp.um,lamda,410)
for Noll in range(1, 36):
        (nz, mz) = lp.noll_to_zern(Noll)
        e_m = 2 if mz == 0 else 1
        A = 1
        #A = math.sqrt((2 * nz + 2) / e_m / math.pi)
        #print(e_m)
        if lp.__version__ == '2.0.5': #or greater actually
            #latest LightPipes has inverted argument order for usability
            # and adds the optional "units" kwarg
            Field2 = lp.Zernike(Field2, nz, mz, 10*lp.um, zernike_poly[Noll] * A, units='lam')
        else:
            A = 1*lamda
            Field2 = lp.Zernike(nz, mz, 10*lp.um, zernike_poly[Noll] * A, Field2)
phase2 = lp.Phase(Field2)
phase2 = lp.PhaseUnwrap(phase2)

plt.imshow(phase2)

grafik

Some addition comments:

Let us know if this solves your problem. Best, Lenny