File: C%2B%2B_Tutorial.md

package info (click to toggle)
meep-mpi-default 1.17.1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 51,672 kB
  • sloc: cpp: 29,881; python: 17,210; lisp: 1,225; makefile: 477; sh: 249; ansic: 133; javascript: 5
file content (225 lines) | stat: -rw-r--r-- 11,376 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
---
# C++ Tutorial
---

Instead of using the [Python interface](Python_User_Interface.md), Meep is also callable as a C++ library by writing a C++ program that links to it. The C++ interface provides the most flexibility in setting up simulations. There are 19 examples in the [tests/](https://github.com/NanoComp/meep/tree/master/tests) subdirectory of the source repository which cover a wide range of functionality. We should also note that, while Meep is nominally in C++, it is perhaps better described as "C+". That is, most of the coding style is C-like with a few C++ features.

[TOC]

Differences from libctl
-----------------------

The C++ interface has several differences from the libctl interface besides the obvious difference in syntax.

The most notable difference is that, while the libctl interface puts the origin (0,0,0) at the *center* of the cell, the C++ interface by default puts the origin at the *corner* of the cell. That is, an $L\times L\times L$ cell goes from ($-L/2$,$-L/2$,$-L/2$) to ($L/2$,$L/2$,$L/2$) in libctl, but from (0,0,0) to ($L$,$L$,$L$) in C++. This can be changed by calling `grid_volume::shift_origin`.

Overview
--------

We begin with a brief outline of a Meep C++ program, with minimal explanations, leaving more details for the examples below. The C++ program should begin with:

```c++
#include <meep.hpp>
using namespace meep;
```

to include the definitions of the Meep routines. Later, when you compile (see below), you must also link to the Meep libraries.

Your main program should then initialize Meep, and will generally then define a computational `grid_volume` and the associated `structure` describing the geometry and materials, initialize the `fields`, add sources, and then time-step. In short:

```c++
int main(int argc, char **argv) {
  initialize mpi(argc, argv); // do this even for non-MPI Meep
  double resolution = 20;     // pixels per distance
  grid_volume v = vol2d(5, 10, resolution); // 5x10 2d cell
  structure s(v, eps, pml(1.0));
  fields f(&s);

  f.output_hdf5(Dielectric, v.surroundings());

  double freq = 0.3, fwidth = 0.1;
  gaussian_src_time src(freq, fwidth);
  f.add_point_source(Ey, src, vec(1.1, 2.3));
  while (f.time() < f.last_source_time()) {
     f.step();
  }

  f.output_hdf5(Hz, v.surroundings());

  return 0;
}
```

This example doesn't do much &mdash; it just runs a Gaussian source and outputs the $H_z$ field at the end. The dielectric structure is determined by the user-defined function `eps`, which has the form:

```c++
double eps(const vec &p) {
  if (p.x() < 2 && p.y() < 3)
    return 12.0;
  return 1.0;
}
```

which returns the dielectric function $\varepsilon(\mathbf{x})$ which is just a 2$\times$3 rectangle of ε=12 in the upper-left corner. Unlike in the Scheme interface, by default the origin of the coordinate system is at the *corner* of the cell.

Now that you have the basic flavor, we can proceed to some more specific examples.

Computing the Quality Factor of a Resonator
-------------------------------------------

In this first tutorial, we will write the script to compute the quality factor of a 1d Fabry-Perot cavity. For a 1d system, Meep considers a cell along the $z$ coordinate. The control file will be a C++ file having extension \*.cpp. In order to use all the classes and subroutines available in Meep, the first two lines of any control file must be the following:

```c++
#include <meep.hpp>
usingnamespace meep;
```

The particular Fabry-Perot cavity we will investigate consists of an air region bounded by two distributed Bragg reflectors which are quarter-wave stacks of ε of 12 and 1. We choose the size of the defect to be twice as large as the air thickness in the quarter-wave stack, so that a defect mode is found near midgap. This structure will include $N$=5 periods of the Bragg reflector on either side of the defect. We can use a larger $N$ but the quality factor may then be too large to compute. The parameters are set up as follows:

```c++
const double eps1 = 12.0; // epsilon of layer1
const double eps2 = 1.0;  // epsilon of layer 2
const double grating_periodicity = 1.0;
const double d1 = sqrt(eps2) / (sqrt(eps1)+sqrt(eps2)); // quarter wave stack dimensions
const double d2 = sqrt(eps1) / (sqrt(eps1)+sqrt(eps2));
const double half_cavity_width = d2;
const int N = 5;
```

Meep supports perfectly matching layers (PML) as absorbing boundary conditions. The PML begins at the edge of the computational volume and works inwards. Hence, we specify the size of the cell as follows: 

```c++
const double pml_thickness = 1.0;
const double z_center = half_cavity_width + N*grating_periodicity + pml_thickness;
```

Note that `z_center` is half the cell length. To specify a dielectric structure, we define a function that takes as input one parameter, a position vector, and returns the value of the dielectric at that position.

```c++
double eps(const vec &p) {
 vec r = p - vec(z_center);
 if (abs(r.z()) < half_cavity_width)
   return 1.0;
 else {
   double dz = abs(r.z()) - half_cavity_width;
   while (dz > grating_periodicity)
     dz -= grating_periodicity;
   return (dz < d1) ? eps1 : eps2;
 }
}
```

We are now ready to set up the cell, excite the sources, time step the fields, and compute the resulting quality factor. Here we set up the main part of the control file incorporating some of Meep's classes and sub routines. We will excite the mode at the midgap of the bandgap, which we expect to have the largest quality factor since it will have the largest exponential decay.

```c++
int main(int argc, char **argv) {
 initialize mpi(argc,argv);
 const double amicron = 10.0;
 const grid_volume vol = vol1d(2*z_center,amicron);
 structure s(vol,eps,pml(pml_thickness));
 fields f(&s);
```

Note the constructor for the `grid_volume` class in 1d which takes as parameters the size of the cell and the resolution. The sources in Meep are excitations of the polarization vector in $\mathbf{D}=\varepsilon\mathbf{E}+\mathbf{P}$. The polarization can be any one of the six cartesian or cylindrical fields. There are a variety of sources including dipole and current sources, gaussian pulses and a continuous wave sources. For now, we use a gaussian source centered at the midgap frequency with a narrow width, along with the start time and end time of the source.

```c++
 const double w_midgap = (sqrt(eps1)+sqrt(eps2))/(4*sqrt(eps1)*sqrt(eps2));
 gaussian_src_time src(w_midgap, 1.0, 0.0, 5.0);
 f.add_point_source(Ex, src, vol.center());
```

Here we make use of the built in function, `vol.center()`, that automatically computes the geometric center of the of the computational volume. To see what the unit cell of the dielectric function looks, output as an HDF5 file, we invoke the built in command:

```c++
f.output_hdf5(Dielectric, v.surroundings())
```

This versatile command can be used to visualize all of the field components, field energy when called at a particular time step. The resulting output file will be `eps-000000.00.h5` and to view it, we first need to convert it to the portable network graphics (PNG) format with the `h5topng` tool. We are now ready to begin the time stepping of the fields, and do so in a simple loop:

```c++
while (f.time() < f.last_source_time()) f.step();
```

To calculate the quality factor, we need to monitor a field beyond the time at which our point source has been turned off. We can do this in Meep by establishing an array where all fields at a particular point can be monitored. We set up the area as well as the output arrays we will need later as such:

```c++
int maxbands = 5;
int ttot = int(400/f.dt)+1;
complex`<double>` *p = new complex`<double>`[ttot];
complex`<double>` *amps = new complex`<double>`[maxbands];
double *freq_re = new double[maxbands];
double *freq_im = new double[maxbands];
```

The variable `maxbands` is the number of mode frequencies we will be looking for while `ttot` represents the total number of timesteps for which we will monitor the field component at. For this simulation given the large quality factor that we expect, we will time step for a long period of time which in this case is 400 unit seconds. Now we are ready to time step and collect the field information:

```c++
 int i=0;
 while (f.time() < f.last_source_time() + 400) {
   f.step();
   p[i++] = f.get_field(Ex,vol.center());
}
```

The `get_field` function does exactly that, obtains the value of the field at a given position within the cell using linear interpolation where necessary. Now we have all the information we need and must obtain the frequencies of the modes which we do by invoking a special tool for harmonic inversion, `harminv`, to extract the real and imaginary frequencies:

```c++
 int num = do_harminv(p, ttot, f.dt, 0.8*w_midgap, 1.2*w_midgap, maxbands, amps, freq_re, freq_im);
```

The integer returned by `harminv` is the number of mode frequencies obtained from the input data `p`. The particular call to `harminv` included passing the arrays by value and telling `harminv` to look for frequencies within 20% of the mid gap frequency up to a maximum of 5 bands. At this point, the necessary information to compute the quality has been stored in the `freq_re` and `freq_im` arrays and we compute the quality factor using the formula, $Q=-\omega_r/2\omega_i$. All that is left to do, is to print these results with the quality factor:

```c++
master_printf("frequency,amplitude,quality factor\n");
for (int i=0; i<num; ++i)
  master_printf("%0.6g%+0.6gi,%0.5g%+0.5gi,%g\n",freq_re[i],freq_im[i],real(amps[i]),imag(amps[i]),-0.5*freq_re[i]/freq_im[i]);
return 0;
}
```

That's it, we are done. The output for this program is `3.26087e+06`. The large quality factor is to be expected since our system includes a relatively high number of Bragg reflectors ($N$=5) in each direction and we are exciting the mode at mid gap. Suppose we want to see what the resonant mode looks like, say over one period. The following block of code time steps the field for exactly one period while outputting the $E_x$ field for the cell at each time step:

```c++
double curr_time = f.time();
while (f.time() < curr_time + 1/w_midgap) {
 f.output_hdf5(Ex, vol.surroundings());
 f.step();
}
```

After we convert the HDF5 files to PNG format and superimpose the images on the dielectric background to produce an animated, we obtain the following:

<center>
![](images/Fabryperot.gif)
</center>

Compiling
---------

In order to compile your code and link against the Meep library, you must link in several additional libraries that Meep requires. There are three possible ways to do this:

(1) After compiling the Meep package following the instructions elsewhere, place foo.cpp in the `tests/` subdirectory, cd to the same directory, and invoke:

```sh
make foo.dac
```

Run the resulting executable via:

```sh
./foo.dac
```

(2) Use the [pkg-config](https://en.wikipedia.org/wiki/pkg-config) program which is installed by default on most Linux systems:

```sh
g++ `pkg-config --cflags meep` foo.cpp -o foo `pkg-config --libs meep`
```

Naturally, replace `g++` with the name of your C++ compiler if you are not using the GNU compilers.

(3) Compile with g++, this time invoking each library separately:

```sh
g++ -malign-double foo.cpp -o foo -lmeep -lhdf5 -lz -lgsl -lharminv -llapack -lcblas -latlas -lfftw3 -lm
```