M. T. Homer Reid MIT Home Page
Physics Problems Research My Music About Me Miscellany


Codes





libRWG and libTDRT: Libraries for Boundary-Element Analysis of 3D and 2D Electromagnetic Scattering Problems

libRWG and libTRDT are C++ libraries for use in developing codes for boundary-element analysis of 3D (libRWG) and 2D (libTDRT) electromagnetic scattering problems. These libraries implement algorithms for (1) reading in geometry files describing mesh discretizations of 3D and 2D objects (scatterers), (2) numerically evaluating the integrals needed to compute the elements of the matrices and vectors involved in standard BEM formulations of EM scattering problems (the EFIE and PMCHW formulations), and (3) performing a variety of postprocessing and visualization techniques, including computing the fields scattered from objects illuminated by given sources.

libRWG and libTDRT include support for both real frequencies (oscillatory fields, i.e. radar problems, antenna problems, and other ordinary electromagnetic scattering problems) and imaginary frequencies (decaying fields, as used e.g. in numerical computations of Casimir interactions).

libRWG and libTDRT are named after the basis functions they support. libRWG works with the "RWG" basis functions introduced by Rao, Wilton, and Glisson (IEEE Transactions on Antennas and Propagation AP-30 409 (1982)). libTDRT works with Two Dimensional Roof-Top functions. For a detailed description of these basis functions and their use in solving electromagnetic scattering problems, see the overview section below.

Although libRWG and libTDRT use separate code bases, their structures and APIs are sufficiently similar that we can describe them both together in this document. Most users will only need to use one or the other depending on whether the scattering problem in question is of fundamentally 3D or 2D character.

libRWG and libTDRT work with objects and geometries. An object is a discretized representation of a single, isolated, compact scatterer. (libRWG and libTDRT don't do the discretization for you -- you do that on your own, using external meshing software, and supply the resulting meshfiles to libRWG/libTDRT). A geometry is simply a collection of objects, which may be displaced or rotated relative to one another using methods supplied by libRWG/libTDRT.

The algorithms implemented by libRWG/libTDRT can then be divided into two granularity levels, described by two portions of the API:

  • a high-level or geometry-level API, containing algorithms that operate on a geometry as a whole, and
  • a low-level API, containing algorithms that operate on individual objects in the geometry and the subelements of which those objects are comprised.

Roughly speaking, if you just want to use libRWG/libTDRT to solve scattering problems and compute the scattered fields, you will only need to use the high-level API. If you want to use these libraries to develop your own new methods for solving scattering problems, you will want to use the low-level API.



Table Of Contents

  1. A Overview of RWG and TDRT Basis Functions and Their Use in Solving Electromagnetic Scattering Problems
  2. High-level API
    1. 2.1. Creating a geometry
    2. 2.2. Computing the BEM matrix for scattering problems
    3. 2.3. Computing the RHS vector for scattering problems
    4. 2.4. Solving scattering problems
    5. 2.5. Computing scattered fields
    6. 2.6. Computing Dyadic Green's Functions and Stress Tensors
    7. 2.7. Transforming a Geometry
    8. 2.8. Exporting to MATLAB
    9. 2.9. Visualization
    10. 2.10. Computing Induced Dipole Moments
    11. 2.11. A complete real-frequency example: Scattering from spheres and cubes
    12. 2.12. A complete imaginary-frequency example: Computing the vacuum expectation value of the electromagnetic stress-energy tensor
  3. Low-level API
    1. 3.1. Creating an object
    2. 3.2. Computing matrix elements





1. An Overview of RWG and TDRT Basis Functions and Their Use in Solving Electromagnetic Scattering Problems

This discussion is sufficiently involved that I think I will put it into a PDF document instead of trying to do in on a webpage:

RWG and TDRT Basis Functions





2. High-Level API

C++ code files that access libRWG and/or libTDRT must #include the appropriate header files.

      #include <libRWG.h>
      #include <libTDRT.h>
    



2.1 Creating a Geometry

The primary object exported by libRWG is a C++ class called RWGGeometry (for libTDRT, it's TDRTGeometry). Before doing anything else, you should create an instance of one of these classes. The class constructor takes a single string argument, the name of a geometry file describing the geometry.


      RWGGeometry *R;
      TDRTGeometry *T;

      R=new RWGGeometry("Spheres.rwggeo");
      T=new TDRTGeometry("Cylinder.tdgeo");
    
    
For details on the format of the .rwggeo and .tdgeo files used to specify 3D and 2D geometries, see GeometryFiles.



2.2 Creating the BEM matrix for scattering problems

The first step in solving a scattering problem is to form the BEM matrix at a given frequency (in the 3D case) or at a given frequency and z-wavevector (in the 2D case). (For brevity in what follows we will abbreviate this full phrase to "at a given frequency.")

The RWGGeometry and TDRTGeometry classes offer class methods for assembling the BEM matrices, at real or imaginary frequencies, in either the EFIE or PMCHW formulations. The first step is to call AllocateBEMMatrix() to allocate a matrix of the size appropriate for the BEM matrix. (The return value of this routine is a pointer to a type called HMatrix, which is a very simple little wrapper class I put together to abstract away some of the details of how matrices are stored and manipulated.) After you have allocated a matrix, you call AssembleBEMMatrix() to fill it in.

Class Methods for Allocating and Assembling BEM Matrices
Dimension Class Methods
3D
 HMatrix *RWGGeometry::AllocateBEMMatrix(int RealFreq);

 void RWGGeometry::AssembleBEMMatrix(double Frequency, int RealFreq, 
                                     int nThread, HMatrix *M);
2D
 HMatrix *TDRTGeometry::AllocateBEMMatrix(int RealFreq);

 void TDRTGeometry::AssembleBEMMatrix(double Frequency, int RealFreq, 
                                      double q, int nThread, HMatrix *M);

The various parameters in these routines are summarized in the following table.

Parameters in BEM Allocation / Assembly Routines
Parameter Type Description
Frequency double Real or imaginary frequency (Omega or Beta) depending on the value of the RealFreq flag.
RealFreq int Set RealFreq=1 for real-frequency problems, RealFreq=0 for imaginary-frequency problems. (libRWG.h and libTDRT.h define constants REAL_FREQ and IMAG_FREQ, with values of 1 and 0 respectively, for use as values for the RealFreq parameter to library routines.)
q double z wavevector for 2D routines.
nThread int The number of threads spawned to assemble the matrix. Should be set less than or equal to the number of available cores on your machine.
M HMatrix * Pointer to an instance of the HMatrix class. This is storage for the BEM matrix, with details about the size of the matrix, and whether its entries are real or complex, abstracted away. The matrix must be created by a call to AllocateBEMMatrix before it can be filled in by AssembleBEMMatrix.



2.3 Computing the RHS vector for scattering problems

The next step in solving a scattering problem is to construct the RHS vector (the vector we called B in the memo). This requires the user to supply a routine for computing the incident field at an arbitrary point. This routine must have the following prototype:
 
      void EHFunc(double *R, void *UserData, cdouble *EH); 
    
where the parameters are described in the following table:

Parameters to EHFunc Routine
Parameter Input/Output Description
R Input Cartesian coordinates of point at which incident fields are to be evaluated. R[0..2]=x,y,z.
UserData Input May be used to pass any additional parameters your incident-field routine may need.
EH Output On return, EH[0..2] must contain the 3 Cartesian components of the incident electric field at R. For PMCHW routines, EH[3..5] must also contain the 3 Cartesian components of the incident magnetic field at R.

Two particularly common types of incident field are (a) plane waves and (b) the field of a point source (electric or magnetic dipole). My little auxiliary library libIncField contains prefabricated EHFunc functions for each of these situations, so most users will never have to write their own EFFunc routine.

(For those who do, we pause to note the following with regard to the user-supplied EHFunc routine:

  • There is no explicit frequency parameter to EHFunc. If your incident field depends on frequency, you will need to pass that information using the UserData mechanism. (And nobody will check to make sure that the frequency you use when assembling the RHS is the same as the frequency you used to assemble the BEM matrix, so be careful.)
  • If all the objects in your geometry are perfect metals, the BEM formulation of the scattering problem makes no reference to the incident magnetic field, and hence your incident field routine may leave EH[3..5] uninitialized. (Otherwise, your incident field routine must fill in all 6 slots in the EH vector.)
  • For 3D imaginary-frequency problems, the incident fields are real-valued. In this case the imaginary components of EH are ignored.)
To simplify function prototypes, libRWG.h and libTDRT.h define a type for pointers to EHFunc functions:
 
      typedef void (*EHFuncType)(double *R, void *UserData, cdouble *EH); 
    
Once you have created your EHFunc routine, you can allocate space for, and initialize, an RHS vector using routines similar to those you used to allocate and assemble the BEM matrix. RHS vectors are stored using a data type named HVector, designed for interoperation with HMatrix.

Class Methods for Allocating and Assembling RHS Vectors
Dimension Class Method
3D
HVector *RWGGeometry::AllocateRHSVector(int RealFreq);

void RWGGeometry::AssembleRHSVector( EHFuncType EHFunc, void *UserData, int RealFreq,
                                     int nThread, HVector *B);
3D
HVector *TDRTGeometry::AllocateRHSVector(int RealFreq);

void TDRTGeometry::AssembleRHSVector( EHFuncType EHFunc, void *UserData, int RealFreq,
                                      int nThread, HVector *B);



2.4 Solving scattering problems

Once you have assembled the BEM matrix and the RHS vector for a given incident field, you must solve the linear system to compute the induced current distribution on the object surfaces. libRWG and libTDRT don't care how you do this, but the most direct way is to use the following class methods provided by the HMatrix class:

  
      void HMatrix::LUFactorize();
      int HMatrix::LUSolve(HVector *X);
    

The first of these replaces an HMatrix with its LU-factorization as computed by LAPACK. The second uses the LU-factorization to solve a linear system with RHS vector X. (The solution vector is overwritten on the input vector, so that when the routine returns X is the solution to the system).

It is important to note that you only need to form and factorize the BEM matrix once at a given frequency. Once you have done this, you can solve the scattering problem for any number of incident fields without recomputing the matrix (indeed, this fact is one of the key virtues of the BEM in the first place). You will only need to recompute and refactorize the BEM matrix if you change the frequency or apply geometrical transformations to the geometry (see below).

Here is a code snippet illustrating how you might solve the scattering problem for several different incident fields. This is for a 3D imaginary-frequency problem.


      RWGGeometry *G;
      HMatrix *M;
      HVector *X;
      int nThread;
      double Beta;

      Beta=0.1;
      nThread=GetNumProcs();

      /* create the geometry */
      G=new RWGGeometry("Spheres.rwggeo");

      /* allocate, assemble, factorize BEM matrix */ 
      M=G->AllocateBEMMatrix(IMAG_FREQ);
      G->AssembleBEMMatrix(Beta, IMAG_FREQ, nThread, M);
      M->LUFactorize();

      /* form RHS for first incident field and solve scattering problem */
      X=G->AllocateRHSVector(IMAG_FREQ);
      G->AssembleRHSVector(MyEHFunc1, MyUserData1, IMAG_FREQ, nThread, X);
      M->LUSolve(X);

      /* do various things with the solution to that problem ... */

      /* form RHS for second incident field and solve scattering problem */
      G->AssembleRHSVector(MyEHFunc2, MyUserData2, IMAG_FREQ, nThread, X);
      M->LUSolve(X);

      /* now do various things with the solution to _that_ problem ... */
    



2.5 Computing scattered fields

After you have solved a scattering problem to obtain the solution vector K, you can evaluate the scattered fields anywhere in space using libRWG/libTDRT class methods.

Class Methods for Computing Scattered Fields
Dimension Class Method
3D
void RWGGeometry::GetFields( double *X, int WhichObject, double Frequency, int RealFreq,
                             HVector *X, int nThread, cdouble *EH);

void RWGGeometry::GetFields( double *X, double Frequency, int RealFreq,
                             HVector *X, int nThread, cdouble *EH);
2D
void TDRTGeometry::GetFields( double *X, int WhichObject, double Frequency, int RealFreq, double q,
                             HVector *X, int nThread, cdouble *EH);

TDRTGeometry::GetFields( double *X, double Frequency, int RealFreq, double q,
                             HVector *X, int nThread, cdouble *EH);

Parameters in Scattered Field Routines
Parameter Type Description
X double[3] Cartesian coordinates of evaluation point. X[0..2]=x,y,z.
WhichObject int If the evaluation point X lies in the interior of one of the scattering objects in the geometry, you must set WhichObject equal to the index of that object (0 if the object is the first object listed in the .rwggeo file, 1 if the second object, etc.). If the evaluation point does not lie in the interior of a scattering object, set WhichObject=-1, or use the second of the two function prototypes above (in which the WhichObject parameter does not appear).
Frequency double Real or imaginary frequency (Omega or Beta) depending on the value of the RealFreq flag.
RealFreq int Set to 1 or 0 to select real or imaginary frequency.
q double z wavevector for 2D routines.
X double *, cdouble * Linear BEM system solution vector.
nThread int The number of threads spawned to compute the fields. Should be set less than or equal to the number of available cores on your machine.
EH double[6], cdouble[6] On entry, EH must point to a buffer with space for 6 double (3D imaginary frequency) or cdouble (all others) values. On return, EH[0..2] are the three Cartesian components of the scattered electric field at the evaluation point X, and EH[3..5] are the components of the scattered magnetic field.



2.6 Computing Dyadic Green's Functions and Stress Tensor VEVs

The electric and magnetic dyadic Green's functions are defined to be the electric and magnetic fields arising from unit-strength electric and magnetic sources. It is easy enough to compute these by doing six separate libRWG scattering calculations. For the special case in which the source and destination points in the Green's function coincide, the six-scattering-calculation procedure is encoded in a libRWG routine. (Needless to say, we are talking here about the dyadic Green's functions with the vacuum component subtracted off, as is natural for boundary-element analysis, so that G(x,xp) is finite as xxp.)

The prototype is

      void RWGGeometry::GetGij(double *R, HMatrix *M, HVector *KN, 
                               double Frequency, int RealFreq, 
                               int nThread,
                               cdouble GE[3][3], cdouble GM[3][3]);
where
  • R[0..2] are the cartesian components of the evaluation point
  • M is the LU-factorized BEM matrix
  • KN is a scratch vector: you have to allocate KN via a call to AllocateRHSVector(), but you don't have to do anything else to it
  • Frequency, RealFreq, nThread are as in other routines described above
  • GE and GM must be user-allocated 3x3 arrays of cdoubles. On return, GE[i][j] and GM[i][j] are the (i,j) components of the electric and magnetic dyadic Green's functions at spatial point R (i.e. both spatial arguments of the Green's function are set equal to R.) Note that GE[i][j] is the i component of the scattered electric field at R due to a point electric current source at R pointing in the j direction.

The next obvious requirement for Casimir applications is a routine that evaluates the (vacuum expectation value of the) Maxwell (van der Waals) stress tensor at a point in space. (This is done by evaluating the electric and magnetic dyadic Green's functions and combining them appropriately.)

The libRWG routine for doing this is

      void GetTijNj(double R[3], double nHat[3], 
                    HMatrix *M, HVector *KN, 
                    double Frequency, int RealFreq,
                    int nThread, 
                    double TE[3], double TM[3]); 
where
  • R[0..2] are the cartesian components of the evaluation point
  • nHat[0..2] are the cartesian components of the vector normal to the bounding surface over which we are integrating at R.
  • M is the LU-factorized BEM matrix
  • KN is a scratch vector allocated by a call to AllocateRHSVector()
  • Frequency, RealFreq, nThread are as in other routines described above
  • TE and TM are the cartesian components of the force-per-unit-area on an infinitesimal patch of surface at R with surface normal nHat.



2.7 Transforming a Geometry



2.8 Exporting to MATLAB

For some purposes it is useful to be able to play with the BEM system matrices and vectors in MATLAB. For this purpose you can use the ToMatlab() class method provided by both HMatrix and HVector. This class method takes a string argument and produces a .m file that you can execute at the MATLAB command line to import the matrix or vector in question:

      HMatrix *MyMatrix;
      HVector *MyVector;

      ...

      MyMatrix->ToMatlab("M");
      MyVector->ToMatlab("V");
This will produces files named Load_M.hd5, Load_V.hd5 and Load_M.m, Load_V.m in your current working directory. The former are binary files containing the actual data, while the latter are M-files you can execute at the MATLAB prompt:
      >> Load_M;
      >> Load_V;

Now your MATLAB session contains a matrix named M and a vector named V.

Note that the ToMatlab class method supports printf-style variable argument lists:

      int N=10;
      int Omega=2.0;
      MyMatrix->ToMatlab("M_%i_%g",N,Omega);

This will generate files M_10_2.0.hd5 and M_10_2.0.m.



2.9 Visualization

2.9.1 Visualizing Geometries

2.9.2 Visualizing Induced Current Distributions

RWGGeometry offers a class method named PlotSurfaceCurrents that will produce GMSH files suitable for visualizing the surface currents induced by the incident fields on the scatterers in your geometry.

Class Methods for Visualization
Dimension Class Method
3D
 void *RWGGeometry::PlotSurfaceCurrents(HVector *KN, double Frequency, int RealFreq,
                                        const char *format, ...);

Parameters in Visualization Routines
Parameter Type Description
KN
 HVector * 
The vector of expansion coefficients of the surface current distribution to plot.
Frequency double Real or imaginary frequency depending on the RealFreq parameter.
RealFreq int Set RealFreq=1 for real-frequency problems, RealFreq=0 for imaginary-frequency problems.
format
 char * 
A printf-style format string specifying the filename to which the visualization data will be written. (The file may be subsequently opened in GMSH for viewing). Any additional arguments needed by the format string are passed immediately after format.



2.10 Computing Induced Dipole Moments

RWGGeometry offers a class method named GetDipoleMoments for computing the dipole moments (electric and magnetic) induced by the incident field on the objects in your scattering geometry.

void RWGGeometry::GetDipoleMoments(double Frequency, int RealFreq, HVector *KN, int nThread, cdouble (*PM)[6]);
The first four parameters are inputs. For the PM parameter, you pass in a two-dimensional array of dimension (number of objects in geometry) x (6). In other words, declare this array like this:
cdouble PM[G->NumObjects][6];
On return, PM is filled in as follows:
  • PM[0][0..2] are the x, y, z components of the electric dipole moment induced on the first scattering object in your geometry (the first OBJECT in the .rwggeo file.)
  • PM[0][3..5] are the x, y, z components of the magnetic dipole moment induced on the first scattering object in your geometry.
  • PM[1][0..2] are the x, y, z components of the electric dipole moment induced on the second scattering object in your geometry ,
  • etc.



      2.11 A complete real-frequency example: Radar cross-sections of arbitrary objects

      This program takes two command-line arguments: an .rwggeo file and a (real) frequency. The program illuminates the scattering geometry with plane waves traveling in the positive z direction and (a) linearly polarized with E-field in the x direction, (b) linearly polarized with E-field in the y direction, and (c) left-circularly polarized. For each polarization, the program solves the BEM system, generates a .pp file that may be opened in GMSH to view the induced current distribution, and generates a .out file containing RCS data that may be plotted in gnuplot.

      /*
       * PlotRCS.cc  -- an example program to demonstrate how to use the 
       *             -- real-frequency algorithms in libRWG to compute the 
       *             -- radar cross section for scattering of a plane wave
       *             -- from an arbitrary scattering geometry
       * 
       * homer reid  -- 12/2009
       */
      
      #include 
      #include 
      #include 
      #include 
      #include 
      
      #include 
      #include 
      #include 
      
      #include 
      
      /***************************************************************/
      /* constants ***************************************************/
      /***************************************************************/
      #define DTHETA 0.1   // angular increment at which to plot RCS
      #define R_RCS  25.0  // radius at which to plot RCS
      #define R_RCS2 (R_RCS*R_RCS)
      
      /***************************************************************/
      /* main function   *********************************************/
      /***************************************************************/  
      int main(int argc, char *argv[])
      {
        RWGGeometry *G;
        HMatrix *M;
        HVector *KN_XPol, *KN_YPol, *KN_CPol;
        PlaneWaveData PWD;
        int nThread;
        double Omega, Theta, R[3], PV[3];
        double RCS_XPol_Par, RCS_XPol_Perp;
        double RCS_YPol_Par, RCS_YPol_Perp;
        double RCS_CPol_Par, RCS_CPol_Perp;
        FILE *f;
        cdouble EH[6], *E, *H;
        cdouble PM[1][6]; // if geometry had 3 objects, we would say PM[3][6] instead
      
        /***************************************************************/
        /* 1. create the geometry                                      */ 
        /***************************************************************/
        if ( argc!=3 )
         { fprintf(stderr,"usage: %s MyGeometry.rwggeo Omega\n",argv[0]);
           exit(1);
         };
        SetLogFileName("PlotRCS.log");
        G=new RWGGeometry(argv[1]);
        sscanf(argv[2],"%le",&Omega);
      
        printf("Computing RCS for geometry %s at Omega=%g\n",G->GeoFileName,Omega);
      
        nThread=GetNumProcs(); /* GetNumProcs is a routine in libhrutil*/
      
        /***************************************************************/
        /* 2. Initialize acceleration tables to speed up computation   */
        /*    of matrix elements                                       */
        /***************************************************************/
        G->PreCompute(nThread);
      
        /***************************************************************/
        /* 2. assemble and factorize the BEM matrix                    */ 
        /*    (and incidentally export it in MATLAB-readable format)   */
        /***************************************************************/
        M=G->AllocateBEMMatrix(REAL_FREQ);
        G->AssembleBEMMatrix(Omega, REAL_FREQ, nThread, M);
        // M->ToMatlab("%s_%g",GetFileBase(argv[1]),Omega); /* export to matlab*/
        M->LUFactorize();
      
        /***************************************************************/
        /* 3. assemble RHS vectors corresponding to plane waves        */ 
        /*    traveling in the positive Z direction and                */
        /*    (1) polarized with E-field parallel to the X axis        */
        /*    (2) polarized with E-field parallel to the Y axis        */
        /*    (3) circularly polarized (E \propto X + iY )             */
        /***************************************************************/
        PWD.nHat[0]=0.0;         PWD.nHat[1]=0.0;         PWD.nHat[2]=1.0;
        PWD.Frequency=Omega;
        PWD.RealFreq=REAL_FREQ;
        PWD.Eps=PWD.Mu=1.0;
      
        PWD.E0[0]=1.0;         PWD.E0[1]=0.0;             PWD.E0[2]=0.0;
        KN_XPol=G->AllocateRHSVector(REAL_FREQ);
        G->AssembleRHSVector(EHPlaneWave, (void *)&PWD, REAL_FREQ, nThread, KN_XPol);
      
        PWD.E0[0]=0.0;         PWD.E0[1]=1.0;             PWD.E0[2]=0.0;
        KN_YPol=G->AllocateRHSVector(REAL_FREQ);
        G->AssembleRHSVector(EHPlaneWave, (void *)&PWD, REAL_FREQ, nThread, KN_YPol);
      
        PWD.E0[0]=M_SQRT1_2;   PWD.E0[1]=1.0fi*M_SQRT1_2; PWD.E0[2]=0.0;
        KN_CPol=G->AllocateRHSVector(REAL_FREQ);
        G->AssembleRHSVector(EHPlaneWave, (void *)&PWD, REAL_FREQ, nThread, KN_CPol);
      
        /***************************************************************/
        /* 4. solve the BEM systems for the three RHS vectors          */ 
        /***************************************************************/
        M->LUSolve(KN_XPol);
        M->LUSolve(KN_YPol);
        M->LUSolve(KN_CPol);
      
        /***************************************************************/
        /* generate plots of induced surface currents and charges      */
        /***************************************************************/
        G->PlotSurfaceCurrents(KN_XPol, Omega, REAL_FREQ, "%s_%g_K_XPol.pp",
                               GetFileBase(G->GeoFileName),Omega);
        G->PlotSurfaceCurrents(KN_YPol, Omega, REAL_FREQ, "%s_%g_K_YPol.pp",
                               GetFileBase(G->GeoFileName),Omega);
        G->PlotSurfaceCurrents(KN_CPol, Omega, REAL_FREQ, "%s_%g_K_CPol.pp",
                               GetFileBase(G->GeoFileName),Omega);
      
        /***************************************************************/
        /*    print induced dipole moments *****************************/
        /***************************************************************/
        G->GetDipoleMoments(Omega, 1, KN_XPol, nThread, PM);
        printf("XPol electric dipole moment: (%+7.3e,%7.3e)i  + (%+7.3e,%7.3e)j  + (%+7.3e,%7.3e)k\n",
                __real__ PM[0][0], __imag__ PM[0][0], 
                __real__ PM[0][1], __imag__ PM[0][1], 
                __real__ PM[0][2], __imag__ PM[0][2]);
        printf("XPol magnetic dipole moment: (%+7.3e,%7.3e)i  + (%+7.3e,%7.3e)j  + (%+7.3e,%7.3e)k\n",
                __real__ PM[0][3], __imag__ PM[0][3], 
                __real__ PM[0][4], __imag__ PM[0][4], 
                __real__ PM[0][5], __imag__ PM[0][5]);
      
        G->GetDipoleMoments(Omega, 1, KN_YPol, nThread, PM);
        printf("YPol electric dipole moment: (%+7.3e,%7.3e)i  + (%+7.3e,%7.3e)j  + (%+7.3e,%7.3e)k\n",
                __real__ PM[0][0], __imag__ PM[0][0], 
                __real__ PM[0][1], __imag__ PM[0][1], 
                __real__ PM[0][2], __imag__ PM[0][2]);
        printf("YPol magnetic dipole moment: (%+7.3e,%7.3e)i  + (%+7.3e,%7.3e)j  + (%+7.3e,%7.3e)k\n",
                __real__ PM[0][3], __imag__ PM[0][3], 
                __real__ PM[0][4], __imag__ PM[0][4], 
                __real__ PM[0][5], __imag__ PM[0][5]);
      
        G->GetDipoleMoments(Omega, 1, KN_CPol, nThread, PM);
        printf("CPol electric dipole moment: (%+7.3e,%7.3e)i  + (%+7.3e,%7.3e)j  + (%+7.3e,%7.3e)k\n",
                __real__ PM[0][0], __imag__ PM[0][0], 
                __real__ PM[0][1], __imag__ PM[0][1], 
                __real__ PM[0][2], __imag__ PM[0][2]);
        printf("CPol magnetic dipole moment: (%+7.3e,%7.3e)i  + (%+7.3e,%7.3e)j  + (%+7.3e,%7.3e)k\n",
                __real__ PM[0][3], __imag__ PM[0][3], 
                __real__ PM[0][4], __imag__ PM[0][4], 
                __real__ PM[0][5], __imag__ PM[0][5]);
      
        /***************************************************************/
        /* 5. plot the RCS on a half-circle of radius R_RCS (=100)     */
        /*    centered at the origin and lying in the XZ plane         */
        /***************************************************************/
        f=vfopen("%s_%g.rcs","w",GetFileBase(argv[1]),Omega);
        for(Theta=0.0; Theta<=M_PI; Theta+=DTHETA)
         { 
            R[0]=R_RCS*sin(Theta);
            R[1]=0.0;
            R[2]=R_RCS*cos(Theta);
            fprintf(f,"%e ",Theta);
      
            G->GetFields(R, Omega, REAL_FREQ, KN_XPol, nThread, EH);
            E=EH; H=EH+3;
            RCS_XPol_Par = R_RCS2*( __real__ ( E[0]* ~E[0] ));
            RCS_XPol_Perp= R_RCS2*( __real__ ( E[1]* ~E[1] ));
            fprintf(f,"%e %e %e %e %e %e ",
                       CMag2(EH[0]),CMag2(EH[1]),CMag2(EH[2]),
                       CMag2(EH[3]),CMag2(EH[4]),CMag2(EH[5]));
      
            G->GetFields(R, Omega, REAL_FREQ, KN_YPol, nThread, EH);
            E=EH; H=EH+3;
            RCS_YPol_Par = R_RCS2*( __real__ ( E[0]* ~E[0] ));
            RCS_YPol_Perp= R_RCS2*( __real__ ( E[1]* ~E[1] ));
            fprintf(f,"%e %e %e %e %e %e ",
                       CMag2(EH[0]),CMag2(EH[1]),CMag2(EH[2]),
                       CMag2(EH[3]),CMag2(EH[4]),CMag2(EH[5]));
      
            G->GetFields(R, Omega, REAL_FREQ, KN_CPol, nThread, EH);
            E=EH; H=EH+3;
            RCS_CPol_Par = R_RCS2*( __real__ ( E[0]* ~E[0] ));
            RCS_CPol_Perp= R_RCS2*( __real__ ( E[1]* ~E[1] ));
            fprintf(f,"%e %e %e %e %e %e ",
                       CMag2(EH[0]),CMag2(EH[1]),CMag2(EH[2]),
                       CMag2(EH[3]),CMag2(EH[4]),CMag2(EH[5]));
      
            fprintf(f,"\n");
         };
        fclose(f);
      
        printf("\nThank you for your support.\n");
      }
      
         



      2.12 A complete imaginary-frequency example: Computing the vacuum expectation value of the electromagnetic stress-energy tensor

      /*
       * StressTensor.cc -- an example program to demonstrate how to use the 
       *                 -- imaginary-frequency algorithms of libRWG to 
       *                 -- compute the vacuum expectation value of the 
       *                 -- electromagnetic stress-energy tensor
       * 
       * homer reid      -- 11/2009
       */
      
      #include 
      #include 
      #include 
      #include 
      #include 
      
      #include "libRWG.h"
      #include "libIncField.h"
      
      #include "libhrutil.h"
      
      /***************************************************************/
      /* prototypes for lapack routines ******************************/
      /***************************************************************/
      extern "C" {
      int dpotrf_(const char *uplo, int *n, double *a, int *lda, int *info);
      int dpotrs_(const char *uplo, int *n, int *nrhs, double *a, int *lda, 
                  double *b, int *ldb, int *info);
      }
      
      /***************************************************************/
      /* routine that evaluates the electromagnetic stress-energy    */
      /* tensor.                                                     */
      /* inputs:                                                     */
      /*  G:       pointer to RWGGeometry                            */
      /*  Beta:    imaginary frequency                               */
      /*  M:       BEM matrix as factorized by dpotrf                */
      /*  R:       evaluation point                                  */
      /*  nThread: number of threads to spawn when assembling RHS    */
      /* outputs:                                                    */
      /*  TE[3][3]: electric  stress-energy tensor (TE[i][j]=TE_{ij})*/
      /*  TM[3][3]: matgnetic stress-energy tensor (TM[i][j]=TM_{ij})*/
      /***************************************************************/
      void GetStressTensor(RWGGeometry *G, double Beta, double *M, double R[3], 
                           int nThread, double TE[3][3], double TM[3][3])
      {
        static int NBF;
        static double *X=0;
      
        int iOne=1, info, i, j;
        struct PointSourceData MyPSD, *PSD=&MyPSD;
        double EH[6];
      
        /* on first invocation only, allocate memory to store the RHS vector */
        if (X==0)
         { NBF=G->GetDimension();
           X=(double *)malloc(NBF*sizeof(double));
         };
      
        /* configure some properties of the point source */
        memcpy(PSD->X0, R, 3*sizeof(double)); /* point source is located at R */
        PSD->FreqType=LIF_IMAG_FREQ;          /* point source has imag frequency */
        PSD->Frequency=Beta;                  /*  and that frequency is beta     */
      
        /*------------------------------------------------------------------*/
        /*- step 1: compute all components of electric stress-energy tensor */
        /*------------------------------------------------------------------*/
        PSD->SourceType=LIF_TYPE_PSEC;   /* for this, point source is a PSEC */
        for(j=0; j<3; j++)
         {
           /* set point source to point in the j direction */
           memset(PSD->nHat,0,3*sizeof(double));
           PSD->nHat[j]=1.0;
      
           /* form RHS vector using incident field of point source */
           G->AssembleRHS_IF(EHPointSource, (void *)PSD, nThread, X);
      
           /* solve the BEM system to compute current source BF weights */
           dpotrs_("U", &NBF, &iOne, M, &NBF, X, &NBF, &info);
      
           /* compute the scattered electric field back at X */
           G->GetFields_IF(R, Beta, X, nThread, EH);
      
           /* the three components of the electric field at X are the */
           /* jth column of the TE matrix                             */
           for(i=0; i<3; i++)
            TE[i][j] = EH[i];
         };
      
        /*------------------------------------------------------------------*/
        /*  step 2: compute all components of magnetic stress-energy tensor */
        /*------------------------------------------------------------------*/
        PSD->SourceType=LIF_TYPE_PSMC;   /* now the point source is a PSMC */  
        for(j=0; j<3; j++)
         {
           /* set point source to point in the j direction */
           memset(PSD->nHat,0,3*sizeof(double));
           PSD->nHat[j]=1.0;
      
           /* form RHS vector using incident field of point source */
           G->AssembleRHS_IF(EHPointSource, (void *)PSD, nThread, X);
      
           /* solve the BEM system to compute current source BF weights */
           dpotrs_("U", &NBF, &iOne, M, &NBF, X, &NBF, &info);
      
           /* compute the scattered electric field back at X */
           G->GetFields_IF(R, Beta, X, nThread, EH);
      
           /* the three components of the magnetic field at X are the */
           /* jth column of the TH matrix                             */
           for(i=0; i<3; i++)
            TM[i][j] = EH[i+3];
         };
        
      }
      
      /***************************************************************/
      /* main function   *********************************************/
      /***************************************************************/  
      int main()
      {
        RWGGeometry *G;
        int i, NBF, nThread, info;
        double Beta;
        double *M, R[3], TE[3][3], TM[3][3];
      
        /***************************************************************/
        /* 1. create the geometry                                      */ 
        /***************************************************************/
        G=new RWGGeometry("Plates.rwggeo");
        Beta=0.1;
      
        nThread=GetNumProcs(); /* GetNumProcs is a routine in libhrutil*/
      
        /***************************************************************/
        /* 2. assemble and factorize the BEM matrix                    */ 
        /***************************************************************/
        NBF=G->GetDimension();
        M=(double *)malloc(NBF*NBF*sizeof(double));
        G->AssembleM_IF(Beta,nThread,M);
        dpotrf_("U", &NBF,  M, &NBF, &info);
        if (info)  /* this should only happen in case of defective geometries */
         ErrExit("BEM matrix was not positive definite");
      
        /***************************************************************/
        /* 3. get the stress tensors at R=(1,2,3) **********************/
        /***************************************************************/
        R[0]=1.0;
        R[1]=2.0;
        R[2]=3.0;
        GetStressTensor(G, Beta, M, R, nThread, TE, TM);
        
        /***************************************************************/
        /* print results ***********************************************/
        /***************************************************************/
        printf("Electric stress-energy tensor: \n");
        for(i=0; i<3; i++)
         printf("%+7.3e %+7.3e %+7.3e\n",TE[i][0],TE[i][1],TE[i][2]);
        printf("Magnetic stress-energy tensor: \n");
        for(i=0; i<3; i++)
         printf("%+7.3e %+7.3e %+7.3e\n",TM[i][0],TM[i][1],TM[i][2]);
      
        printf("\nThank you for your support.\n");
      }
      
         





      3. Low-Level API



      3.1 Creating an Object



      3.2 Computing Matrix Elements


libRWG and libTDRT, by Homer Reid
Last Modified: 05/08/11