- Home
- User Articles
- How to Write User-Defined Sources and Scatter Functions in Fortran
- Home
- Programming ZEMAX
- Extensions
- How to Write User-Defined Sources and Scatter Functions in Fortran
- Home
- Non Sequential Ray Tracing
- Sources, Splitting and Scattering
- How to Write User-Defined Sources and Scatter Functions in Fortran
How to Write User-Defined Sources and Scatter Functions in Fortran
- By Jeff Casey
- Published 10 December 2007
- User Articles , Extensions , Sources, Splitting and Scattering
-
Rating:




User-Defined Objects
Another useful user element is the “USER OBJECT”, which allows you to generate an arbitrary surface or solid object. The compiled DLL file must be placed in the following directory on the root disk for ZEMAX to find: {ZEMAXroot}\ Objects \ DLL \ UserObjects.
Generation of a User Object is very similar to generation of a source. The main routine is necessarily more complicated, as it must serve several key functions:
- it must generate the rendering and solid model information (as a surface of adjacent triangles),
- it must annotate these triangular areas by surface/solid, grating, and CSG flags,
- it must calculate the details of an intersection when passed the information for a ray,
- it must calculate polarization data, when asked.
The User Object code has two ZEMAX calls (exported, case sensitive): UserObjectDefinition and UserParamNames. The header and comments for UserObjectDefinition are:
function UserObjectDefinition (data, tri_list)
implicit none
integer*4, dll_export :: UserObjectDefinition
real*8 data(0,*), tri_list (0:*)
! for the call to UserObjectDefinition, the data is stored as follows:
! data(1) = calling mode (see below)
! data(2-4) = x,y,z position for origin of ray traced
! data(5-7) = cx,cy,cz direction cosines of ray traced
! data(8) = exact code (see below)
! data(10...) = returned values (depends on mode)
! data(100) = number of parameters passed to DLL
! data(101...) = first parameter in list, next, ...
! Return value is normally 0 on success, -1 on failure.
! if called with data(1) = 0: compute rendering and solid model info
! return total # of triangular faces for rendering in data(10)
! return solid/shell flag in data(11) (solid=1, surface=0)
! return grating info in data(12) (not=0, yes, return CSG #)
! return coating code in data(20) (default=0, compute coating info locally=1)
! if called with data(1) = 1: generate rendering mesh
! return filled array of tri_list, real*8 array 10*N long
! (10 values per triangle)
! sequence for each triangle: (x1,y1,z1,x2,y2,z2,x3,y3,z3,code)
! code is 6 digit integer EECCRV, decimally parsed:
! V = visibility: 0=all legs, 1=1-2 inv, 2=2-3 inv,
! 4=3-1 inv...sum for multiples
! for rectangles, make common leg of two triangles invisible
! R = 0 for refractive, 1 for reflective
! CC = coat scatter group...only 00 through 03 supported at this time
! EE = exact code: 0 = triangle is exact solution, other values are used to
! tag which internal algorithm may be used to calculate intersection
! ALSO, must return # of triangular faces again in data(10)
! if called with data(1) = 2: find intersection and normal vector to surface
! data(2,3,4) = ray origin
! data(5,6,7) = ray direction cosines
! data(8) = exact code, tells which algorithm to use
! find the intersection by determining path length of ray, S
! intersection at (x+Scx, y+Scy, z+Scz)
! return S in data(10)
! return normal vector to surface at intersection point in data(11,12,13)
! if ray misses, return errorcode -1
! if called with data(1) = 3: do coating calculation
! data(2,3,4) = ray origin
! data(5,6,7) = ray direction cosines
! data(8) = CSG (coating/scatter group)
! data(9,10,11) = normal vector at surface
! data(12,13) = current and next indices of refraction
! data(14) = cosine of norm incidence angle
! data(15) = wavelength in um
! data(20) = zero flag (set to 1 if routine is to calculate coating,
! otherwise uses object settings)
! return data(20) = 1
! S-polarization amplitudes in data(21,22,23,24) (refl_r, refl_i, xmit_r, xmit_i)
! P-polarization amplitudes in data(31,32,33,34) (refl_r, refl_i, xmit_r, xmit_i)
! (don't forget that amplitude factor is sqrt of intensity factor)
! return error code -1 if this DLL doesn't do coating calcs
! if called with data(1) = 4: do initialization with safe data
! return values into data(101....) as default values
The coding of UserObjectDefinition is dominated by algorithmic concerns. Most of the above comments and specifications were taken from the ZEMAX manual and from other examples on the ZEMAX knowledge base. Not all options have been tested to date, but a variety of surfaces have been generated and found to work quite well.
It is most certain that your coding of a surface will require a root finder, a normal vector generator, and perhaps several other functions. Remember to code all of these as re-entrant (recursive), and make no system calls from within your function.
The coding of UserParamNames is very similar to that used for Sources. One example is shown mostly complete below:
function UserParamNames (data)
implicit none
integer*4, dll_export :: UserParamNames
character*10 data
integer i1, i2, i3
i1 = ichar(data(1:1))
i2 = ichar(data(2:2))
i3 = ichar(data(3:3))
data = char(0)
if (i2 .eq. 0) then
if (i1 .eq. 48) then ! 0
data = "All Faces" // char(0)
else if (i1 .eq. 49) then ! 1
data = "param 1" // char(0)
else if (i1 .eq. 50) then ! 2
data = "param 2" // char(0)
.......
end if
else if ((i3 .eq. 0) .and. (i1 .eq. 49)) then
if (i2 .eq. 48) then ! 10
data = "param 10" // char(0)
else if (i2 .eq. 49) then ! 11
data = "param 11" // char(0)
........
end if
end if
UserParamNames = 0
return
end function
For UserParamNames, we have parsed the input data a bit differently this time (also clumsily). In this case, we can parse out more than 10 parameters, plus we must handle a pointer less than or equal to zero. For the USER OBJECT types, ZEMAX will call UserParamNames with a zero or negative pointer to get the names of the CSG (coating scatter group). For this example, we have are using a surface in 3D, which will be described by a single CSG, thus we pass “All Faces” back for a pointer of 0.