How to write a Grid Sag DAT file programmatically

This article provides Mathematica and Matlab code for generating a Grid Sag DAT file in the format required by OpticStudio. This format is the same as what is used for the Grid Phase surface. The code does not require a connection to OpticStudio.

Authored By Alexandra Culler & Erin Elliott


Article Attachments


A DAT file provides the defining surface characteristics for Grid Sag and Grid Phase surfaces in Sequential Mode. For OpticStudio to properly read in the data from the DAT file, the file must follow the format discussed in the Knowledgebase article "How to use the Grid Sag surface type."

Manually typing information into a DAT file can be tedious and time-consuming, especially for the larger files that are usually required for higher-accuracy surfaces. Instead, the preferred method for generating a DAT file is through an external program such as Matlab or Mathematica.

In this article, we will provide code that can be used to generate a DAT file for use in OpticStudio.

Data set definition and assumptions

For the sake of this demonstration, we will generate a sag profile based solely on the standard sag equation, as shown below:

This equation is discussed in detail in the Help System file "The Setup Tab...Editors Group (Setup Tab)...Lens Data Editor...Sequential Surfaces (lens data editor)...Standard".

The sag profile will be based on the Surface 1 in the attached file "Simple_Singlet_Original.zar" which is attached to this article. It has the following properties:

  Radius Thickness Material Coating Clear Semi-Diameter Chip Zone Mech Semi-Diameter Conic
Surface 1 59.74 12.00 N-BK7   12.62 0 12.62 0

We can take the reciprocal of the "Radius" to get the curvature, c, for the sag equation. Then we can also plug in the conic constant to find:

Where x and y are the cartesian representation of the coordinates across the surface, from 0 to the edge as defined by the Clear Semi-Diameter.

DAT file definition

As discussed in "How to use the Grid Sag surface type" and in the Help System, the DAT file will require a declaration of the amount of points we’re using in addition to the spacing between points. These values are generally arbitrary. In this demonstration, we will match the amount of pixels to one of the default resolutions in OpticStudio: 513x513.

Additionally, we know that the lens specifies a Clear Semi-Diameter (CSD) of 12.62 mm. Since we know how many points we want in either direction (513) and the maximum radial coordinate of the lens (12.62) we can calculate the required space between points to be:

Our goal is to use the above surface information and defining characteristics of the DAT file to exactly match the sag profile of Surface 1 in "Simple_Singlet_Original.zar." That profile should match what is currently given in the Analyze…Surface…Sag analysis tool, as shown below:

To begin, we will write a 2D array representing the sag data. The data will represent the Z-value of the sag function at a given value of x and y:

Next, the data will be flattened so that it can be written line-by-line to the DAT file.

We will perform these functions in Mathematica and Matlab. The code for both is attached. The resulting DAT file will be identical between the two programs.

Data and DAT file generation in Mathematica

Data generation

Based on the discussion above, we will set the following definitions:


radiusOfCurvature = 59.74;

curvature = 1/radiusOfCurvature;

conic = 0;

semiDiameter = 12.62;

resolution = 513;

pointSpacing = (2*semiDiameter)/(resolution-1);


These values may then be used to form the sag equation. This will match the one defined above:


sagFunction[x_, y_] = (curvature * (x^2 + y^2)) / (1 + ((1 + conic) * (curvature)^2 * (x^2 + y^2))^1/2


In this function, we are allowing x and y to be variable so that we can generate a 2D array with 513x513 points, each with a distance in x and y of 0.04929. To load the data into the array, we will use the following:


sagData = Table[sagFunction[x,y],{x,-semiDiameter,semiDiameter,pointSpacing},{y,-semiDiameter,semiDiameter,pointSpacing}];


Before moving on to the file creation, we can ensure that the resulting matrix of data has the distribution we expect by using the ArrayPlot function:



By utilizing the ArrayPlot function, we are checking to make sure the profile behaves similarly to what we see in the Analyze…Surface…Sag tool in OpticStudio. In particular, we are checking for any inconsistencies at the edges of the plot, or any other noticeable artifacts.

Write to the DAT file

As discussed above, we want to flatten the data before attempting to write it to the file. This will allow us to extract one array value at a time and write it to a new line as the “Z” value in the DAT file.

To flatten an array in Mathematica, we can use the following line:


sagDataFlat = Flatten[sagData];


Once we’ve obtained the flattened array sagDataFlat, we can write it to a DAT file with the same name: “sagDataFlat.dat”. We will set it to save in the {Zemax}\Objects\Grid Files folder. We can make sure that Mathematica finds the appropriate folder by using the NETLink module. This will allow us to check the registry for Zemax, which will then let us know where the Zemax data folder is located. This is a similar set of steps to what is found in the ZOS-API boilerplate code. This is accomplished through the following lines:



zemaxData = Registry`GetValue["HKEY_CURRENT_USER\\Software\\Zemax", "ZemaxRoot", -1];

parentPath=FileNameJoin[{zemaxData, "\\Objects"}];

gridPath = FileNameJoin[{parentPath, "\\Grid Files"}];


Once the Grid Files folder is found, the DAT file may be written and placed there through the following set of steps:

filename = FileNameJoin[{gridPath, "\\sagDataFlat.dat"}];

gridsagfile = CreateFile[filename];

line = StringTemplate["`a` `a` `b` `b` 0 0 0"][<|"a" -> resolution, "b" -> pointSpacing|>];

WriteLine[gridsagfile, line];


  templine = ToString[NumberForm[sagDataFlat[[i]], Infinity, ExponentFunction -> (Null &)]] <> " 0 0 0 0";

  WriteLine[gridsagfile, templine];

  , {i, 1, Dimensions[sagDataFlat][[1]]}];


We can break down that code block in the following way:

Code snippet Purpose Output
filename The name – including the path – of the DAT file we want to generate <path>\Zemax\Objects\Grid Files\sagDataFlat.dat
gridsagfile This is set equal to the command to make a new file. The lines following will populate this blank file. Blank DAT file

This allows us to write the data to the file with the proper layout using the StringTemplate command. We are telling the first line to follow the header format required by DAT files. 

Variable a will be equal to the resolution. Variable b will be equal to the point spacing. 


Do loop

This iterates through the flattened array of data. The "templine" command is where we store the current iteration's array value, followed by four zeroes.

The values given by "templine" are written to the "gridsagfile" with the "WriteLine" command.

Once the above code is run, we will have a DAT file located in the {Zemax}\Objects\Grid Files folder which may be immediately imported into OpticStudio. 

Data and DAT file generation in Matlab

Data generation

To begin the sag generation, we will declare the necessary variables. These include the Semi-Diameter, Curvature, and Conic constant. We will also declare the variables for the resolution:

% Initial surface 1 information

SemiDiameter = 12.62;

Radius = 59.74;

Curvature = 1/Radius;

Conic = 0;


% Intended resolution (npix) and point spacing (dx)

npix = 513;

dx = 2*SemiDiameter / (npix-1);

Using these variables, we can begin building the matrix to store the sag data. To start, generate a "blank" 513x513 matrix:

sagMatrix = zeros(npix);

In order to populate this matrix with the correct values of x and y, we will need to generate two additional arrays. These arrays will have a dimension of 1x513 and will be populated with evenly-spaced values of x and y from -12.62 to 12.62:

xvals = linspace(-SemiDiameter,SemiDiameter,npix);

yvals = linspace(-SemiDiameter,SemiDiameter,npix);

With the appropriate matrices created, we can write a loop to generate the sag data. The loop will use the values from the xvals and yvals arrays to fill the sagMatrix with the sag data as given by the standard sag equation above.

The full loop is below:

for col = 1:length(sagMatrix)

    for row = 1:length(sagMatrix)

        sagMatrix(col, row) = Curvature*(xvals(col)^2 + yvals(row)^2)/(1+(sqrt(1-(1+Conic)*(Curvature^2)*(xvals(col)^2 + yvals(row)^2))));



In short, z(x,y) is translated to be sagMatrix(xvals(col), yvals(row)).

At this point, we have a 513x513 matrix populated with the sag values. We can confirm that the matrix emulates the profile we are looking for graphically.

By doing so, we are checking to make sure the profile behaves similarly to what we see in the Analyze…Surface…Sag tool in OpticStudio. In particular, we are checking for any inconsistencies at the edges of the plot, or any other noticeable artifacts.

Once we have confirmed that the profile matches what we're looking for, we can flatten the matrix into a 1D array. This will make it easier to pull values into the DAT file with a loop. 

Surf1DatClean = sagMatrix(:);

Now, we have a flat array of data from which we can pull the "z" values of the DAT file. We can begin the generation of the DAT file by first building the path to the {Zemax}\Objects\Grid Files folder. We can do this in a similar method to the ZOS-API: by finding the Zemax {DATA} folder within the Windows Registry. Once located, we can append the path to direct to the appropriate folder within. The full path name will be given by "gridDir":

zemaxData = winqueryreg('HKEY_CURRENT_USER', 'Software\Zemax', 'ZemaxRoot');

gridDir = '\Objects\Grid Files\';

Once the appropriate path name is defined, we can write to the DAT file "sagData.dat" with the following code:

filename1 = fullfile(zemaxData, gridDir, 'sagData.dat');

fileID = fopen(filename1, 'wt');

fprintf(fileID, '%d %d %1.10g %1.10g %1.f %1.f %1.f', npix, npix, dx, dx, 0, 0, 0);

fprintf(fileID, '\n');

for i = 1:size(Surf1DatClean-1)

    fprintf(fileID, '%0.20g %d %d %d %d\n', Surf1DatClean(i), 0, 0, 0, 0);



The code above may be broken down in the following way:

Code snippet Purpose Output
filename1 The name - including the path - of the DAT file we want to generate <path>\Zemax\Objects\Grid Files\sagData.dat
fileID This is set equal to the command to make a new file with writing privileges. The lines following will populate the blank file. Blank file
Line 3 - fprintf This declares the format for the file that will match the format required for the first line of the DAT file. Then, the values for the first line will be printed. The first line uses npix and dx to define the resolution and point spacing of the subsequent data. 
for loop This iterates through the flattened array of data. The first value will correspond to the sag ("z") value. Each line will add 4 zeroes before completing. 

Replace the Standard Surface

With the DAT file located within the {Zemax}\Objects\Grid Files folder, it is ready for use in OpticStudio. We can directly replace Surface 1 in the file "Simple_Singlet_Original.zar" by converting the surface type to "Grid Sag" and changing the Radius value to "Infinity". Also ensure that the Conic value is set to "0".

Then, navigate to Surface 1 Properties…Import. You should find the newly-generated DAT file in the drop-down menu as it is located in the Grid Files folder. Import the DAT file to the surface. If needed, a full procedure for conversion may be found in "How to use the Grid Sag surface type." 

Once it is imported, open the Surface Sag plot under Analyze…Surface…Sag. The plot should approximately match the image generated in either program. The resulting system performance should be near-identical. If it is not, try increasing the resolution of the DAT file.

The file featuring the Grid Sag surface in the place of Surface 1 is attached as "Simple_Singlet_GridSag.zar."


Was this article helpful?
4 out of 5 found this helpful



Article is closed for comments.