How to add radial ripple to a sequential surface using the ZOS-API

This article provides Mathematica and Matlab code to replace a sequential surface with a Grid Sag. The Grid Sag Surface will feature a radially-symmetric ripple pattern with an RMS and spatial frequency specified by the user. This functionality is especially useful in representing mid-spatial frequency error caused by diamond-turned manufacturing.

Authored By Alexandra Culler & Erin Elliott


Article Attachments


In this series, we have learned how to import a Grid Sag surface manually, and how to generate a DAT file programmatically. Then, in "How to convert any sequential surface to a Grid Sag surface" we showed how to use the ZOS-API to pull surface information and replace a surface with an identical Grid Sag. Now, we will combine those tools to impart a radially-symmetric surface ripple to a surface within the system for use in counteracting potential fabrication errors.

Diamond-turning is a typical method of fabrication for aspheres and freeform surfaces. However, this type of manufacturing can increase error on the mid-spatial frequency level, imparting what is known as "surface ripple".

Mid-spatial frequency (MSF) error is error with a period that falls between 2 mm and 0.2 mm. MSF error is especially tricky as it cannot be easily controlled. In the case of high-spatial frequency error, a scattering of light occurs that can be treated as loss within the system. Similarly, low-spatial frequency error refers to form errors that can usually be counter-acted through the use of Zernike decomposition. MSF error, on the other, cannot be controlled with Zernike terms, nor can it be treated as a simple loss to the system [1]. Currently, the best way to deal with this type of error is to apply it as a tolerance within the system. One way to do so is using a Grid Sag surface.

Notes about article series and assumptions made in this tutorial

This is the fourth article in a series of Knowledgebase article tutorials on utilizing the Grid Sag surface and the API. As such, this article will build on knowledge from the previous tutorials. In particular, the attached code will be using modules (Mathematica) and functions (Matlab) to simplify redundant steps. These modules and functions have been defined in "How to convert any sequential surface to a Grid Sag surface" and include:

  • View the LDE (displayLDE)
  • View the System Explorer (displaySystemData)
  • Check sag profile (getSag)
  • Generate sag array (getExtendedSag)
  • Generate a DAT file (makeDatFile)

In Matlab, the functions are available as a separate class file that is loaded into the code with the following declaration:


MyFunctions = KBA4Tools;


To use this class, make sure that 'KBA4Tools.m' is within the same folder as the rest of the code. To call a function within the class, the following syntax will be used:


myNewVariable = MyFunctions.functionName( Inputs )


Open and investigate the "Cooke 40 degree field.zmx" sample file

To begin, the attached API programs will check for the existence of an API folder in the native {Zemax}\Samples folder. If the folder doesn't exist, then one will be created.

Once complete, the API will open the "Cooke 40 degree field.zmx" file found in {Zemax}\Samples\Sequential\Objectives and will save it as a new file in the appropriate API folder. The new file name will be "Cooke 40 degree field_Surf1_GridSag_AddRipple.zmx".

To confirm that we have successfully pulled the file into the API, we will use the Lens Data Editor and System Explorer modules to look at the current system.

Identify the current system performance

In "How to convert any sequential surface to a Grid Sag surface" we manually checked the system performance within OpticStudio. To do so, we viewed the output of the Standard Spot Diagram and the Wavefront Map with the following settings:

  • Access the Spot Diagram by navigating to Analyze…Rays & Spots…Standard Spot Diagram. Update the ray density to 25.
  • Access the Wavefront Map by navigating to Analyze…Wavefront…Wavefront Map. Update the resolution to 512x512.

Now, we will use the ZOS-API to pull the data instead. The tools above are accessed through the ZOSAPI.Analysis namespace. We can use the ZOSAPI.Analysis.Settings.IAS_ interface to update each window to match the declared settings before pulling the data.

Once extracted, the data is formatted as below:

And the output of the Wavefront Map will be given:

Now we will be able to monitor the changes to the system caused by the ripple function we impart. This will be particularly useful when testing multiple surface deformations.

Generate and add radial ripple perturbation

Pull the initial sag information

We require a resolution value with which we intend to extract and write the sag information. As in "How to convert any sequential surface to a Grid Sag surface," we will match this resolution to one of the OpticStudio-provided values: 512. This resolution will be used to generate a point spacing based on the size of the surface of interest. These declarations are made below:


surfaceNumber = 1;

resolution = 512;

surfaceSemiDiameter = theLDE.GetSurfaceAt(surfaceNumber).SemiDiameter;

pointSpacing = (2 * surfaceSemiDiameter) / resolution;


Using the provided function getSag, we can pull a plot of the surface sag into the API. Then, we can generate an array of sag data using the function getExtendedSag:

The matrix "startingSag" is used for a qualitative analysis of the surface. The matrix "initialSagData" will be used in the DAT file and combined with the ripple we generate.

Ripple inputs

Mid-spatial frequency error presents as a surface ripple -  a deviation from a perfect sphere. To properly represent it, we must have:

  • The height of the ripple; in other words, the RMS or PV deviation.
  • The frequency of the ripple.

In this tutorial, we will be using the following equation to generate the ripple:


  • a = The amplitude of the ripple. This is related to the targeted RMS surface deviation
  • b = The targeted spatial frequency of the ripple
  • x and y = The location on the surface of interest

We will begin with a targeted RMS error of 0.1 waves and a targeted spatial frequency of 5.263 (given by dividing 50 rings by the semi-diameter).

Using the rippleEquation, we will generate a matrix of data representing values at different points along the surface. Values x and y will be the locations on the surface, each subsequent location separated by the pointSpacing value from before. Then, a will be a guess value (1) and will be the period of the cosine function – calculated by multiplying the targeted spatial frequency by 2π:


targetRMS = 0.1;

targetSpatialFrequency = 50 / surfaceSemiDiameter;

convertRMS = targetRMS * (testWavelength * 10^-9) * 1000;

avalGuess = 1;

bval = 2 * (PI) * targetSpatialFreq;


Because the amplitude is not directly linked to the RMS of the surface deviation, we must begin with a guess value. Then, the proper amplitude can be iteratively calculated from the RMS of the resulting ripple matrix. For a simple Cosine like what we are using, we will find that only one iteration is required.

As in "How to convert any sequential surface to a Grid Sag surface," we use the linspace functions to generate arrays of x and y values in Matlab:


xdir = linspace(-surfaceSemiDiameter, surfaceSemiDiameter, (resolution + 1));

ydir = linspace(-surfaceSemiDiameter, surfaceSemiDiameter, (resolution + 1));


for col1 = 1:length(rippleEquation)

    for row1 = 1:length(rippleEquation)

        rippleEquation(col1, row1) = avalGuess * cos(sqrt((xdir(col1)^2)+(ydir(row1)^2)) * bval);



Next, using the code below, we can find the current RMS of the ripple. Then, it can be compared to the targeted RMS and a new aval may be generated from the ratio.

In Matlab:

flatRipple = rippleEquation(:);

flatRipple = flatRipple(flatRipple~=0);

currentRMS = sqrt(var(flatRipple));


We will only calculate a new aval if the currentRMS is not equal to convertRMS. Once those values are the same, we can combine the testRipple matrix with the initial sag profile:


newSurfaceSag = initialSagData + testRipple;


Replace surface 1 and check the new system

The matrix newSurfaceSag is what we will use to generate our DAT file and replace the surface within the already-built system. From here, we are following the same procedure as is outlined in "How to convert any sequential surface to a Grid Sag surface." As such, we will use a module/function to automate the creation of the DAT file.

To begin, we will choose the name for our file – surf1Ripple:


% Apply to the file

filename = "surf1Ripple.dat";


Now we may generate the DAT file and import it into the system. The output of the makeDatFile function will be used as the filename for the DAT file. With this, the output may be placed directly into the import function.


surfaceData = MyFunctions.makeDATFile(newSurfaceSag, filename, resolution + 1, pointSpacing);


surface = TheLDE.GetSurfaceAt(surfaceNumber);

surfaceTypeSettings = surface.GetSurfaceTypeSettings(ZOSAPI.Editors.LDE.SurfaceType.GridSag);


surface.Radius = 0;

surface.Conic = 0;




Using the displayLDE function/module, we can ensure that the file was properly loaded:



We can also confirm that the ripple has been added by pulling the new sag information directly from OpticStudio and subtracting out the initial sag information:

Finally, we may observe the new system specifications by pulling in the Spot Diagram and Wavefront Map data. The results are below:

As expected, the results are different from our nominal system. This is due to the change in image location caused by the ripple pattern. We can view the updated layout by saving and opening the file:

For a full tolerance of surface ripple, it may be appropriate to apply some compensation before extracting the performance data.

Generating functions for future automation

As discussed in "How to convert any sequential surface to a Grid Sag surface," it is convenient to create modules and functions for repetitive tasks or tasks you intend to perform in multiple systems. After this tutorial, we may create five new functions:

  • getSpotDiagramInfo: Used to open and extract information from the Standard Spot Diagram.
  • getWFE: Used to open and extract data from the Wavefront Map.
  • calculateRMS: Used to find the RMS of a given array.
  • generateRipple: Used to write a radially symmetric ripple function to an array.
  • addRippleToSurface: Uses functions "getExtendedSag" and "generateRipple" to pull the sag data of a targeted surface and update it with surface ripple perturbation.

The five new modules will be located at the end of the attached Mathematica file in section 8. The five new functions will be provided within the function file FullModuleTools.m.

Notes about Mathematica syntax

  • In Mathematica, the modules are not provided in a separate document, but are instead loaded in the beginning of the document.
  • In Mathematica, we can generate a matrix of values with the Table function:

    testRipple = Table[rippleEquation[avalGuess, bval, x, y], {x, -surfaceSemiDiameter, surfaceSemiDiameter}, {y, -surfaceSemiDiameter, surfaceSemiDiameter}];
  • In Mathematica, the process for testing the RMS output and checking the amplitude value is given by the following:

    flatRipple = Flatten[DeleteCases[testRipple, 0, Infinity]];

    currentRMS = (Variance[flatRipple])^(1/2)

    aval = convertRMS / currentRMS

Alternate application of the ripple

While this article does not present this option, it is possible to apply the ripple data without first extracting the original surface sag. The Grid Sag surface will allow for the definition of surface sphere, conic asphere, polynomial asphere, or Zernike asphere terms. If the original surface has these terms (Radius, Conic, or extra data parameters) applied, they may be copied to the Grid Sag surface parameters. Then, the DAT file will only need to include the ripple data as the "additional sag" to be added to the surface.


[1] Richard N. YoungworthBenjamin B. Gallagher, and Brian L. Stamper "An overview of power spectral density (PSD) calculations", Proc. SPIE 5869, Optical Manufacturing and Testing VI, 58690U (18 August 2005);


Was this article helpful?
0 out of 0 found this helpful



Article is closed for comments.