# Simulate 2D diffraction grating using customized diffractive DLL

In OpticStudio, currently we only support one dimensional grating. However, it's not difficult to simulate 2D grating. Here we will show an example using diffractive DLL.

You may compile it with the instructions or use the DLL file attached to this article.

Before we start, here are some articles for required background knowledge that we will not repeat in this forum post.

Let's simply open the attached cpp file and observe how it works in non-sequential mode.

This example is mainly modified from the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c

To compile the program as C++ code. We package the functions with extern 'C'.

In this example, we only want to return diffraction ray direction and its relative intensity. So setting datai31] = 1 is enough. If we also know the polarization state or the full eletric field value, we can use datau31] = 2.

Here assumes we want to simulate 5 orders in x and y direction, so there should be totally 25 orders from (-5,-5) to (5,5). Because currently OpticStudio only considers the diffractive DLL simulates 1D grating, we need to represent the 2D orders in 1D. Here we will only consider orders from 0 to 24, which represents (-2,-2) to (2,2). If OpticStudio requests orders other than this range, we return -1, which will result in an geometric error in OpticStudio. For this reason, when using this DLL, the Start Order should be 0 and Stop Order should be 24 in OpticStudio.

Then, we can decompose the 1D order into corresponding 2D orders.

When data/31] = 1, we need to return the value for phase derivative and phase accumulation.

Note the phase derivative is same as what we described in page 3 in this forum post: How diffraction ray-tracing is calculated | Zemax Community

The phase (variable 'P') is simply accumulation of the phase assuming the phase at surfase vertex is zero.

In UserParamNames(), we define the efficiency for each diffraction order.

Then simply read it to datae30], which is the relative intensity reported to OpticStudio!

### 13 replies

Userlevel 6
+2

Attached is a example for user defined surface used in sequential mode. This is modified from the built-in us_grate.c. The concept is same as above, but with one main difference: there need to be two parameters Order X and Order Y to let users explicitly specify what order to trace. This is because in sequential mode, only one path can be traced at once.

Only two parts are changed.

1. Add two parameters for x directional periodicity.

2. Code added to consider for another periodicity in x direction.

T,M becomes Tx,Ty,Mx,My.

Adding code for dpdx. Just mimics dpdy and that's it!

Userlevel 7
+3

Gotta say Michael, you are doing some seriously good work here. Well done!

Userlevel 6
+2

In OpticStudio, currently we only support one dimensional grating. However, it's not difficult to simulate 2D grating. Here we will show an example using diffractive DLL.

You may compile it with the instructions or use the DLL file attached to this article.

Before we start, here are some articles for required background knowledge that we will not repeat in this forum post.

Let's simply open the attached cpp file and observe how it works.

This example is mainly modified from the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c

To compile the program as C++ code. We package the functions with extern 'C'.

In this example, we only want to return diffraction ray direction and its relative intensity. So setting data[31] = 1 is enough. If we also know the polarization state or the full eletric field value, we can use data[31] = 2.

Here assumes we want to simulate 5 orders in x and y direction, so there should be totally 25 orders from (-5,-5) to (5,5). Because currently OpticStudio only considers the diffractive DLL simulates 1D grating, we need to represent the 2D orders in 1D. Here we will only consider orders from 0 to 24, which represents (-5,-5) to (5,5). If OpticStudio requests orders other than this range, we return -1, which will result in an geometric error in OpticStudio. For this reason, when using this DLL, the Start Order should be 0 and Stop Order should be 24 in OpticStudio.

Then, we can decompose the 1D order into corresponding 2D orders.

When data[31] = 1, we need to return the value for phase derivative and phase accumulation.

Note the phase derivative is same as what we described in page 3 in this forum post: How diffraction ray-tracing is calculated.

The phase (variable 'P') is simply accumulation of the phase assuming the phase at surfase vertex is zero.

In UserParamNames(), we define the efficiency for each diffraction order.

Then simply read it to data[30], which is the relative intensity reported to OpticStudio!

Hi,Michael!

I meet a problem that when setting  data[31] = 2 in the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c and output the new DLL ,  Diffraction Grating with the new DLL in which data[31] = 2 can not receive beam rays, namely the light beams can not reach the diffraction grating.

Why it become like this?

zhouqing

Userlevel 6
+2

Hi Zhouqing,

Thank you for reply. Setting data[31] = 2 should be not a problem. The problem probably happens at your other code. Could you send your question to support inbox so we can help to further look at your question? Thank you!

Michael

I have send my question to the mailbox Zemax Community <community@zemax.com>. In case of encountering problems when sending the email，I also present my questions in more detail as below:
Firstly, I just change the code data[31] = 1 to data[31] = 2  in the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c and output the new DLL.
When I use the new DLL in Diffraction Grating in NSC mode, the light beam can not reach the grating. Maybe there are something wrong when I changed the code in the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c.

Then I review the DLL in ZEMAX Knowledgebase(KBA), there are some keys as shown below:

According to the above content, If I set the code data[31] = 2  , then the data[35]~[37], and data [40]~[45] should also be calculated or set. For the further code setting of  data[35]~[37] and data [40]~[45] , I still feel  a bit difficult to input the perfect new codes as I am not familiar with the C++ and ZEMAX Programming logic for Diffractive DLL.

If Michael could give me some help or suggestions for the above tricky situation as I meet.

Zhouqing

Userlevel 6
+2

Hi Zhouqing,

I’m sorry for confusion, but could you try to create a case via the following link.

https://support.zemax.com/hc/requests/new

Or, you can also send the question to the support box: zemax.support@ansys.com

Both method will reach to the same team. Using the link is suggested as you will be able to check the status of the tickets. Please note you will need to create a Zemax account in order to open a ticket. Please let us know if you have any issue. Thank you.

Thank you, Michael!

I have send my question to the support box: zemax.support@ansys.com

At the same time, I also present my questions again as below:

I just change the code data[31] = 1 to data[31] = 2  in the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c and output the new DLL.When I use the new DLL in Diffraction Grating in NSC mode, the light beam can not reach the grating. Maybe there are something wrong when I changed the code in the built-in sample \Documents\Zemax\DLL\Diffractive\diff_samp_1.c.

Then I review ” Custom DLLs in OpticStudio: An overview of user-defined surfaces, objects, and other DLL types”in ZEMAX knowledgebase (KBA), there are some keys as shown below:

“ If data[31] equals 2, it must also calculate data 35-45, the complete output ray data. The DLL should return 0 if successful, and -1 if it encountered an error. For a complete description of all the data items passed between ZOS and the Diffraction DLL, please see the example {Zemax}\DLL\Diffraction\diff_samp_1.c.

Note when the data[31] is 2, the power of diffracted ray is mainly calculated by the returned field data in data[40-45]. However, the data[30] can still affect the ray-tracing in two ways. One is that OpticStudio will use data[30] for estimating the Lost Energy, which shows in the Ray Trace dialog, during raytracing. One is that the diffractive ray will stop tracing if the value in data[30] is smaller than Minimum Relative Ray Intensity in the System Explorer.

According to the above content,  If I set the code data[31] = 2  , then the data[35]~[37], and data [40]~[45] in the built-in sample diff_samp_1.c should also be calculated. For the further code setting of  data[35]~[37] and data [40]~[45] , I still  feel  a bit difficult to write the perfect new codes as I am not familiar with the C++ and ZEMAX Programming logic for Diffractive DLL.

If Michael could give me some help or suggestions for the above tricky situation as I present.

Zhouqing

Thanks for the tutorial Michael, very informative.

I have tried to implement a similar code that the one you provide here but to also analize the reflection modes of a simple 2D grating. In my case I restricted the Tranmission and Reflection modes to (-1,-1) → (1,1).

In my case the call for input parameters looks like this:

I have tried to modify data[11] value to accomodate either tranmission and reflection modes asking which mode is being calculated as follows:

but this proven not to work. Zemax only calculated the transmissive modes and all the reflection modes are there but as simple reflection from a glass surface.

Could you please give me a hint on how to implement this properly?

Thank you!

Hi, Michael.

Thanks for your input to the community. I am wondering how the zemax calculate the power of ray, which comes out of the DLL surface (RCWA model for a grating). In the DLL, data [30] represents relative energy of the ray. Here I assum it is diffraction efficiency, which is power flow. Besides, the output electric is also required to give in data [40-45]. Based on my test, the zemax detector seems to calculate the energy of the ray from DLL based on its square module of electric field instead of diffraction efficiency. However, when the diffraction efficiency of first order of light is high, e.g. 99.9%. The square module of electric field is usually larger than 1 because the k-vector changes. Therefore, I have normalize the electric field and multiple square root of the diffraction efficiency to achieve the right number on detector, which is contradictory to the law of wave propagation. Besides, If I did this, the detector energy will change again when this ray from DLL interact with next surface, which is a glass usrface with perfect AR coating. . Do you know what is happening?

Here is my setup. The power on detectors inside glass is 99.941%, which is exactly same as the diffraction efficiency. The power on detector outside of glass is 99.864%. I also tested the AR coating. It is perfect for different incident angles and impossible to influence such 0.1% power variation.

Userlevel 6
+2

Hi Yuqiang,

I feel there are some specific problem in your code and the test. It’s probably best you send your question through support so we can look into details to know the exact issue. However, I guess the following principles can be useful.

1. It’s correct OpticStudio uses [40-45] for the power of the ray not data[30], when data[31] is 2. However, you should still make sure data[30] = sum(data[40]^2+data[41]^2+...data[45]^2)_output / sum(data[40]^2+data[41]^2+...data[45]^2)_input.
2. sum(data[40]^2+data[41]^2+...data[45]^2)_input >= sum(data[40]^2+data[41]^2+...data[45]^2)_output
3. The electric field should be perpendicular to ray direction. Namely, when you return data, you should make sure data[4]*data[40]+data[5]*data[42]+data[6]*data[44]=0 and data[4]*data[41]+data[5]*data[43]+data[6]*data[45]=0

Hope these are useful information.

Hi Yuqiang,

I feel there are some specific problem in your code and the test. It’s probably best you send your question through support so we can look into details to know the exact issue. However, I guess the following principles can be useful.

1. It’s correct OpticStudio uses [40-45] for the power of the ray not data[30], when data[31] is 2. However, you should still make sure data[30] = sum(data[40]^2+data[41]^2+...data[45]^2)_output / sum(data[40]^2+data[41]^2+...data[45]^2)_input.
2. sum(data[40]^2+data[41]^2+...data[45]^2)_input >= sum(data[40]^2+data[41]^2+...data[45]^2)_output
3. The electric field should be perpendicular to ray direction. Namely, when you return data, you should make sure data[4]*data[40]+data[5]*data[42]+data[6]*data[44]=0 and data[4]*data[41]+data[5]*data[43]+data[6]*data[45]=0

Hope these are useful information.

Thanks, Michael.

I will check one by one. Could you share how zemax calculate the energy of ray at different scenarios? It looks like zemax will use relative energy in some situations, but use inner product in other situations.

Best,

Yuqiang

Userlevel 6
+2

Hi Yuqiang,

Do you mean data[30] by relative energy? As discussed above, when data[31] is 2, it’s not used as ray power, but it will be used for judging whether a ray should stop trace because of too low level of power. You can check the following article.

Custom DLLs in OpticStudio: An overview of user-defined surfaces, objects, and other DLL types – Knowledgebase (zemax.com)

Otherwise, I think it works as you expect where it changes the ray power.