Click here to Skip to main content
15,867,141 members
Articles / Desktop Programming / MFC

The Diffraction Grating Calculator

Rate me:
Please Sign up or sign in to vote.
4.40/5 (5 votes)
30 Oct 2010GPL38 min read 41.2K   677   6   11
An MFC Windows program related to Concave Diffraction Gratings using VTK libraries.

Introduction

This is a short introduction to a Physics related piece of software: the Grating Calculator program.

The Grating Calculator program is free software available on SourceForge under the GPL GNU License Version 3.0. The article presents the Physics problem that is solved by the software as well as the use under MFC of the VTK (Visualisation Tool Kit) Libraries as used by the program for 3D graphical representation of physical results. The VTK libs are a 3D rendering engine used in Scientific Visualisation built over the OpenGL graphics base libraries. The version used for the VTK libs is the 5.4.2 VTK Home. The program also makes use of the Charting Control of Cedric Moonen: Charting Control ver. 3.0, available on the CodeProject.

The problem addressed by the program is the following: concave specular (i.e., reflective) diffraction grating is used in research to obtain focal properties in conjunction with a diffractive element. The light reflected by such gratings is focused in addition to being spectrally dispersed. This makes possible to have a single element as the complete optical instrument that diffracts and focuses the light onto a plane or curved surface for recording. For instance, the curved Rowland gratings are the simplest optical elements that combine spectral diffraction with focusing properties. Gratings mounted in the so called Rowland mount (i.e., set-up) have the property to focus spectrally dispersed light in a circle whose diameter is the size of the radius of the concave grating. If the light source is on such a circle (called Rowland circle), then the light is focused onto the same circle after diffraction.

More recently, physicists have started using the so-called Flat Field Spectrometers. Such spectrometers use concave reflective gratings whose line density is not strictly periodical. The grooves on such gratings are ruled with a periodicity that changes from end to end in an aperiodical way. The ruling of such gratings is controlled numerically to adhere to such a prescribed pattern in a very precise way. Such aperiodicity superimposed on the main periodical ruling gives as a result the fact that such gratings are capable of focusing spectrally dispersed light onto surfaces approximately flat, i.e., plain. This is useful as most of the recording sensors nowadays available have flat surfaces (for instance, Charged Coupled Devices - CCDs - as opposed to pellicular films).

The program presented is capable of solving the ray-tracing equations for a general grating, giving the focus position for each dispersed light wavelength in the range sought. The grating is characterised in terms of the aperiodical ruling, and may go from the simple Rowland mount (no aperiodicity) to the most modern flat field ones (controlled aperiodical ruling). The program already recognizes in the set-up parameters two kinds of aperiodically ruled gratings as used in the modern research for soft x-ray spectrometers. The parameters are derived from the works of refs. [1,2,3], where the notion of current day grazing incidence flat field spectrometer is introduced (see also ref. [5]).

Disclaimer: The present article has a thick physical description of the problem solved by the program, and the only code presented is about the use of the VTK 3D libraries in displaying scientific data.

Physics Background

This section illustrates the physical problem solved by the program in physical terms.

Flat field spectrometers in the soft x-ray domain are a well established technology for spectroscopic studies. Concave and plane varied line-space (VLS) gratings providing enhanced spectral image focusing properties are used for synchrotron radiation instrumentation [1], plasma diagnosis [2], and space astrophysics [3]. In the design of a concave grating and in the analysis of its spectral image focusing properties, an analytical method that uses the light path function is usually adopted [4].

If point P is a generic point on the grating with local coordinates u, w, l on its surface (with u as a function of w, l), and A and B are, respectively, a point source and a nominal focal point of a spectrum of wavelength lambda diffracted in the mth order by the grating; then the light path function is defined as:

F = AP + BP + n*m*lambda

where n is the number of grooves at P counted from a reference point O on the grating (which we can assume is the centre of the grating blank).

GratingComp.GIF

The grating set-up for computations

According to Fermat's principle, for point B in the figure to be a stigmatic image of A at the wavelength lambda, it is required that the following differential conditions be satisfied simultaneously:

dF/dw = 0, dF/dl = 0.

With reference again to the figure, we introduce a polar coordinate system and express the distances as:

AP = r<sup>2</sup> + u<sup>2</sup> + l<sup>2</sup> - 2*u*r*cos(alpha) - 2*w*r*sin(alpha)
BP = r'<sup>2</sup> + u<sup>2</sup> + l<sup>2</sup> - 2*u*r'*cos(beta) - 2*w*r'*sin(beta)

where alpha is the angle of incidence, and beta of diffraction from the grating at O.

For a concave grating with a spherical substrate, a point P is defined by the equation:

(u-R)<sup>2</sup> + w<sup>2</sup> + l<sup>2</sup> = R<sup>2</sup>

where R is the radius of the grating. Expanding in a power series, we get:

u = (w<sup>2</sup>+l<sup>2</sup>)/2R + (w<sup>2</sup>+l<sup>2</sup>)<sup>2</sup>/8R<sup>3</sup> + ...

The groove number n on the varied line spaced grating (aperiodically ruled) is defined as:

n = (rho)<sub>0</sub> * ( w + b<sub>2</sub>/R * w<sup>2</sup> + b<sub>3</sub>/R<sup>2</sup> * w<sup>3</sup> + b<sub>4</sub>/R<sup>3</sup> * w<sup>4</sup> + ... )

where (rho)0 is the line density at the centre O of the grating (hence it is the periodical component of the line density), and b2, b3, and b4 are the controlling numerical parameters for the aperiodical part of the ruling of the grating.

Using the equations above, we can express the light path function as a power series of w and l, as follows:

F = r + r' + w*F<sub>10</sub> + w<sup>2</sup>*F<sub>20</sub> + l<sup>2</sup>*F<sub>02</sub> 
        + w<sup>3</sup>*F<sub>30</sub> + w*l<sup>2</sup>*F<sub>12</sub> + w<sup>4</sup>*F<sub>40</sub> 
        + w<sup>2</sup>*l<sup>2</sup>*F<sub>22</sub> + l<sup>4</sup>*F<sub>04</sub> + O(w<sup>5</sup>).

In this equation, every Fij term can be expressed as:

F<sub>ij</sub> = C<sub>ij</sub> + m*lambda*rho<sub>0</sub>*M<sub>ij</sub>

For a complete list of the coefficients Cij and Mij, see ref. [3].

When an aperiodically ruled grating is designed for an imaging spectrometer, the optical layout and the ruling parameters are chosen to minimise aberrations along both the spectral and spatial directions. Stigmatic imaging requires that Fij=0 to the highest possible order.

The condition F10=0 gives the grating equation:

sin(alpha) + sin(beta) = m*lambda*rho<sub>0</sub>

while the focal curve along the spectral dispersion direction is obtained from F20=0 to be used in conjunction with the grating equation:

r' = r * R * cos<sup>2</sup>(beta) / 
    ( r*(cos(alpha)+cos(beta)-2*m*lambda*rho<sub>0</sub>*b<sub>2</sub>) - R*cos<sup>2</sup>(alpha))

This shows that only the linear ruling parameter b2 determines the position of the focal plane, given any optical layout. The two other ruling parameters b3, b4 are often chosen in order to minimise the coma and spherical aberration (F30=0, F40=0, respectively) in the spectral direction at a given wavelength of interest. Particular mountings can also be investigated in order to reduce the aberrations along the spatial direction [3].

To estimate the spectral focusing properties of these spectrometers, a ray-tracing code may be used instead of the equations for Fij=0.

The code determines the focus position by ray-tracing a large number of rays through the grating and following them until the smallest spread is found. For stigmatic imaging from a reflecting surface, the rays should converge to the focus after having travelled an equal amount of time from the source. This is true also for a grating if the optical path length introduced by its elements for constructive interference is taken into account. This is the core of the code: for each ray, the optical path length is corrected by an amount corresponding exactly at the diffraction introduced by the grating in the dispersed ray wavelength. For diffraction to the mth order, this amount corresponds to:

m*lambda*(Number of ruled lines between the point P on grating and reference point O)

I.e., exactly the term on the right of the optical light path function, as defined as:

F = AP + BP + n*m*lambda

As the image is not stigmatic, the smallest geometrical spread of the rays around the focus gives an estimate of the spectral resolution. This resolution that geometric optics predicts, of course, cannot exceed the wave optics limit. The code has been tested against the analytical formulae, and found in good agreement with it in predicting the location of the focus plane.

VTK Rendering in the Code

The most interesting piece of software for CodeProject readers contained in the program is the class that is used to interface the program with the 3D rendering library VTK. Such a class is reproduced here with comments about all its members, describing the use of the VTK 3D graphics library. In an MFC application using VTK, we derive a CView type object from the class CVTKView that itself derives from CView. This allows seamless integration of the VTK rendering engine in an MFC view.

The header of the class follows:

C++
// CVTKView view

#if !defined( __VTK_VIEW__ )
#define __VTK_VIEW__

// VTK headers to include
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkProperty.h"
#include "vtkProperty2D.h"
#include "vtkPropCollection.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkTextProperty.h"

#include "vtkMFCWindow.h"
#include "vtkWin32OpenGLRenderWindow.h"

class CVTKView : public CView
{
    DECLARE_DYNCREATE(CVTKView)
protected:
    CVTKView();   // protected constructor used by dynamic creation
    virtual ~CVTKView();

protected:
   vtkRenderer*   pvtkRenderer;
   vtkMFCWindow*  pvtkMFCWindow;
   bool m_bInitCreate; // to check to avoid duplicating work on New/Open Documents
   bool m_bInitUpdate; // to check to avoid duplicating work on New/Open Documents

public:
   vtkRenderer*  GetRenderer()  { ASSERT(pvtkRenderer); return pvtkRenderer; }
   vtkMFCWindow* GetMFCWindow() { ASSERT(pvtkMFCWindow); return pvtkMFCWindow; };
   // Render the view
   void Render();
   // Override to avoid duplicating work OnCreate on New/Open Documents
   // Call base class first !, use m_bInitCreate
   virtual void OnViewCreate();
   // Override to avoid duplicating work OnInitialUpdate on New/Open Documents
   // Call base class first !, use m_bInitUpdate
   virtual void OnViewInitUpdate();

public:
    virtual void OnDraw(CDC* pDC);      // overridden to draw this view
#ifdef _DEBUG
    virtual void AssertValid() const;
#ifndef _WIN32_WCE
    virtual void Dump(CDumpContext& dc) const;
#endif
#endif

protected:
    DECLARE_MESSAGE_MAP()
public:
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   afx_msg void OnDestroy();
   afx_msg BOOL OnEraseBkgnd(CDC* pDC);
   afx_msg void OnSize(UINT nType, int cx, int cy);
   virtual void OnInitialUpdate();
};

#endif // !defined( __VTK_VIEW__ )

The body of the class follows:

C++
// VTKView.cpp : implementation file

#include "stdafx.h"
#include "VTKView.h"

// CVTKView

IMPLEMENT_DYNCREATE(CVTKView, CView)

CVTKView::CVTKView()
{
   pvtkMFCWindow = NULL;
   pvtkRenderer  = NULL;

   m_bInitCreate = false;
   m_bInitUpdate = false;
}

CVTKView::~CVTKView()
{
}

BEGIN_MESSAGE_MAP(CVTKView, CView)
   ON_WM_CREATE()
   ON_WM_DESTROY()
   ON_WM_ERASEBKGND()
   ON_WM_SIZE()
END_MESSAGE_MAP()

// CVTKView drawing

void CVTKView::OnDraw(CDC* pDC)
{
    CDocument* pDoc = GetDocument();
   if (pvtkMFCWindow)
   {
      // VTK printing support
      if (pDC->IsPrinting())
         pvtkMFCWindow->DrawDC(pDC);
   }
}

// CVTKView diagnostics

#ifdef _DEBUG
void CVTKView::AssertValid() const
{
    CView::AssertValid();
}

#ifndef _WIN32_WCE
void CVTKView::Dump(CDumpContext& dc) const
{
    CView::Dump(dc);
}
#endif
#endif //_DEBUG

// CVTKView message handlers

int CVTKView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   if (CView::OnCreate(lpCreateStruct) == -1)
      return -1;

   // call our virtual create view method
   OnViewCreate();

   m_bInitCreate = true;

   return 0;
}

/*
 * Override to avoid duplicating work OnCreate on New/Open Documents
 * Call base class first !, use m_bInitCreate
 */
void CVTKView::OnViewCreate()
{
   if ( !m_bInitCreate )
   {
      if (pvtkMFCWindow)
      {
         delete pvtkMFCWindow;
         pvtkMFCWindow = NULL;
      }
      // Create the new hosted MFC window
      pvtkMFCWindow = new vtkMFCWindow(this);
      // Create the renderer and add it to window's view
      pvtkRenderer  = vtkRenderer::New();
      if (pvtkMFCWindow)
         pvtkMFCWindow->GetRenderWindow()->AddRenderer(pvtkRenderer);
   }
}

void CVTKView::OnDestroy()
{
   CView::OnDestroy();

   if (pvtkMFCWindow)
   {
      delete pvtkMFCWindow;
      pvtkMFCWindow = NULL;
   }
   if (pvtkRenderer)
   {
      pvtkRenderer->Delete();
   }
}

// Avoid possible flickering
BOOL CVTKView::OnEraseBkgnd(CDC* pDC)
{
   // return CView::OnEraseBkgnd(pDC);
   return TRUE;
}

void CVTKView::OnSize(UINT nType, int cx, int cy)
{
   CView::OnSize(nType, cx, cy);

   // resize our view
   if (pvtkMFCWindow)
      pvtkMFCWindow->MoveWindow(0, 0, cx, cy);
}

void CVTKView::OnInitialUpdate()
{
   CView::OnInitialUpdate();

   // Call our virtual method for initial update
   OnViewInitUpdate();
   Render();

   m_bInitUpdate = true;
}

/*
 * Override to avoid duplicating work OnInitialUpdate on New/Open Documents
 * Call base class first !, use m_bInitUpdate
 */
void CVTKView::OnViewInitUpdate()
{
   if ( !m_bInitUpdate )
   {
      // Add a default interactor to our view
      vtkWin32OpenGLRenderWindow* pRenderWindow = NULL;
      pRenderWindow = pvtkMFCWindow->GetRenderWindow();
      if (pRenderWindow)
      {
         pRenderWindow->GetInteractor()->Initialize();
         pRenderWindow->GetInteractor()->Start();
      }
   }
}

/*
 * Render the view
 */
void CVTKView::Render()
{
   if (!pvtkMFCWindow)
      return;

   vtkWin32OpenGLRenderWindow* pRenderWindow = NULL;
   pRenderWindow = pvtkMFCWindow->GetRenderWindow();
   if (pRenderWindow)
   {
      pvtkRenderer->Render();
      pRenderWindow->Render();
   }
}

VTK works around the concept of a graphical pipeline. The data is processed, starting with source objects that are then filtered (i.e., processed) and finally mapped in an actor of the view.

Source objects -> Filters -> Mappers -> Actors -> Insertion of actors in the Scene

In the derived class from CVTKView, a single method is then used to add the graphical pipeline that displays our data in the VTK derived view:

C++
void CPlotFocalDensity::PlotData()
{
   if ( !m_pFocalSpotObj )
      return;

   CWaitCursor wc;

   int nX, nY; nX = nY = 0;
   m_pFocalSpotObj->GetAttribute(FS_DENSITY_3D_NX)->Get_int(nX);
   m_pFocalSpotObj->GetAttribute(FS_DENSITY_3D_NY)->Get_int(nY);

   CAttribute_Generic *at, *atX, *atY;
   at = atX = atY = NULL;
   at  = m_pFocalSpotObj->GetAttribute(FS_DENSITY_3D);
   atX = m_pFocalSpotObj->GetAttribute(FS_DENSITY_3D_X);
   atY = m_pFocalSpotObj->GetAttribute(FS_DENSITY_3D_Y);

   size_t Naux;
   at->GetSize(Naux);

   // Set-up VTK Source objects
   vtkStructuredGrid* sgrid  = vtkStructuredGrid::New();
   vtkFloatArray*     scalar = vtkFloatArray::New();
   vtkPoints*         points = vtkPoints::New();

   double x, y, z;
   for (size_t j=0; j<Naux; j++)
   {
      atX->GetAt(x,j);
      atY->GetAt(y,j);
      points->InsertPoint(j,x,y,0.0);
       at->GetAt(z,j);
      scalar->InsertValue(j,z);
   }

   sgrid->SetDimensions(nX,nY,1);
   sgrid->SetPoints(points);
   sgrid->GetPointData()->SetScalars(scalar);

   // Create a color lookup table
   vtkLookupTable* lut = vtkLookupTable::New();
   lut->SetNumberOfTableValues(256);
   lut->SetHueRange(0.0,0.667);
   lut->Build();

   // Map the grid datasets
   vtkDataSetMapper*  mapper = vtkDataSetMapper::New();
   mapper->SetInput(sgrid);
   mapper->SetLookupTable(lut);
   double tmp[2];
   sgrid->GetScalarRange(tmp);
   mapper->SetScalarRange(tmp);

   // Apply a contour filter to the data
   vtkContourFilter* contourFilter = vtkContourFilter::New();
   contourFilter->SetInput(sgrid);
   contourFilter->GenerateValues(20,tmp);

   // Map the contour field
   vtkPolyDataMapper* contourMapper = vtkPolyDataMapper::New();
   contourMapper->SetInput(contourFilter->GetOutput());
   sgrid->GetScalarRange(tmp);
   contourMapper->SetScalarRange(tmp);

   // Add the actors in the scene
   if ( carpet )
   {
      pvtkRenderer->RemoveActor(carpet);
      Render();
   }
   carpet = vtkActor::New();
   carpet->SetMapper(mapper);

   if ( contour )
   {
      pvtkRenderer->RemoveActor(contour);
      Render();
   }
   contour = vtkActor::New();
   contour->SetMapper(contourMapper);

   if ( scalarBar )
   {
      pvtkRenderer->RemoveActor(scalarBar);
      Render();
   }
   scalarBar = vtkScalarBarActor::New();
   scalarBar->SetLookupTable(lut);
   scalarBar->SetTitle(_T("Focal Spot Density"));
   scalarBar->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
   scalarBar->GetPositionCoordinate()->SetValue(0.8,0.2);

   // assign our actors to the renderer
   pvtkRenderer->AddActor(carpet);
   pvtkRenderer->AddActor(contour);
   pvtkRenderer->AddActor(scalarBar);
   pvtkRenderer->SetBackground(1.0,1.0,0.4);

   // draw the resulting scene
   pvtkRenderer->ResetCamera();
   pvtkRenderer->GetActiveCamera()->Zoom(3.0);
   pvtkRenderer->GetActiveCamera()->Elevation(45);
   pvtkRenderer->GetActiveCamera()->Azimuth(30);
   pvtkRenderer->GetActiveCamera()->Dolly(1.0);
   pvtkRenderer->ResetCameraClippingRange();
   Render();
}

Points of Interest

Example of using VTK 3D libraries under MFC Visual Studio 2008 SP1 built applications.

Bibliography

  • [1] T. Harada, T. Kita, M. Itou, H. Taira, and A. Mikuni, Nucl. Instrum. Methods A 246, 272 (1986). M. Itou, T. Harada, and T. Kita, Appl. Opt. 28, 146 (1989).
  • [2] T. Kita, T. Harada, N. Nakano, and H. Kuroda, Appl. Opt. 22, 512 (1983). N. Nakano, H. Kuroda, T. Kita, and T. Harada, Appl. Opt. 23, 2386 (1984).
  • [3] T. Harada, H. Sakuma, K. Takahashi, T. Watanabe, H. Hara, and T. Kita, Appl. Opt. 37, 6803 (1998).
  • [4] T. Namioka, J. Opt. Soc. Am. 49, 446 (1959).
  • [5] Instruments division, Hitachi Ltd, 882 Ichige, Hitachinaka-shi, Ibaragi 312− 0033, Japan.

History

  • 27 October 2010 - First draft.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
Italy Italy
Senior Software Developer in C/C++ and Oracle.
Ex-physicist holding a Ph.D. on x-ray lasers.

Comments and Discussions

 
GeneralThanks for sharing Pin
Randor 29-Oct-10 14:59
professional Randor 29-Oct-10 14:59 
GeneralMy vote of 2 Pin
Rick York29-Oct-10 8:13
mveRick York29-Oct-10 8:13 
GeneralRe: My vote of 2 Pin
federico.strati30-Oct-10 22:57
federico.strati30-Oct-10 22:57 
GeneralMy vote of 5 Pin
sam.hill27-Oct-10 17:10
sam.hill27-Oct-10 17:10 
Generaltitle Pin
JohnWallis4227-Oct-10 11:47
JohnWallis4227-Oct-10 11:47 
GeneralRe: title Pin
federico.strati27-Oct-10 20:54
federico.strati27-Oct-10 20:54 
GeneralRe: title Pin
JohnWallis4227-Oct-10 21:57
JohnWallis4227-Oct-10 21:57 
GeneralRe: title Pin
federico.strati27-Oct-10 22:29
federico.strati27-Oct-10 22:29 
QuestionAny screenshots available ? Pin
Maximilien27-Oct-10 4:46
Maximilien27-Oct-10 4:46 
AnswerRe: Any screenshots available ? Pin
federico.strati27-Oct-10 5:07
federico.strati27-Oct-10 5:07 
AnswerRe: Any screenshots available ? Pin
federico.strati27-Oct-10 21:00
federico.strati27-Oct-10 21:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.