forked from john-b-schneider/uFDTD
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fdtd-intro.tex
2185 lines (2004 loc) · 97.8 KB
/
fdtd-intro.tex
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\chapter[Introduction to the FDTD Method]{Introduction to
the Finite-Difference Time-Domain Method: FDTD in 1D
\label{chap:fdtdIntro}}
%\setcounter{page}{1}
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
\footnotetext{Lecture notes by John Schneider. {\tt
fdtd-intro.tex}}
\section{Introduction}
The finite-difference time-domain (FDTD) method is arguably the
simplest, both conceptually and in terms of implementation, of the
full-wave techniques used to solve problems in electromagnetics. It
can accurately tackle a wide range of problems. However, as with all
numerical methods, it does have its share of artifacts and the
accuracy is contingent upon the implementation. The FDTD method can
solve complicated problems, but it is generally computationally
expensive. Solutions may require a large amount of memory and
computation time. The FDTD method loosely fits into the category of
``resonance region'' techniques, i.e., ones in which the
characteristic dimensions of the domain of interest are somewhere on
the order of a wavelength in size. If an object is very small
compared to a wavelength, quasi-static approximations generally
provide more efficient solutions. Alternatively, if the wavelength is
exceedingly small compared to the physical features of interest,
ray-based methods or other techniques may provide a much more
efficient way to solve the problem.
The FDTD method employs finite differences as approximations to both
the spatial and temporal derivatives that appear in Maxwell's
equations (specifically Ampere's and Faraday's laws). Consider the
Taylor series expansions of the function $f(x)$ expanded about the
point $x_0$ with an offset of $\pm\delta/2$:
\begin{eqnarray}
f\!\left(x_0+\frac{\delta}{2}\right) &=&
f(x_0) + \frac{\delta}{2} f'(x_0) +
\frac{1}{2!}\left(\frac{\delta}{2}\right)^2 f''(x_0) +
\frac{1}{3!}\left(\frac{\delta}{2}\right)^3 f'''(x_0) + \ldots,
\\
f\!\left(x_0-\frac{\delta}{2}\right) &=&
f(x_0) - \frac{\delta}{2} f'(x_0) +
\frac{1}{2!}\left(\frac{\delta}{2}\right)^2 f''(x_0) -
\frac{1}{3!}\left(\frac{\delta}{2}\right)^3 f'''(x_0) + \ldots
\end{eqnarray}
where the primes indicate differentiation. Subtracting the second
equation from the first yields
\begin{equation}
f\!\left(x_0+\frac{\delta}{2}\right) -
f\!\left(x_0-\frac{\delta}{2}\right) =
\delta f'(x_0) +
\frac{2}{3!}\left(\frac{\delta}{2}\right)^3 f'''(x_0) + \ldots
\end{equation}
Dividing by $\delta$ produces
\begin{equation}
\frac{f\!\left(x_0+\frac{\delta}{2}\right) -
f\!\left(x_0-\frac{\delta}{2}\right)}{\delta} =
f'(x_0) +
\frac{1}{3!}\frac{\delta^2}{2^2} f'''(x_0) + \ldots
\end{equation}
Thus the term on the left is equal to the derivative of the function
at the point $x_0$ plus a term which depends on $\delta^2$ plus an
infinite number of other terms which are not shown. For the terms
which are not shown, the next would depend on $\delta^4$ and all
subsequent terms would depend on even higher powers of $\delta$.
Rearranging slightly, this relationship is often stated as
\begin{equation}
\left.\frac{df(x)}{dx}\right|_{x=x_0} =
\frac{f\!\left(x_0+\frac{\delta}{2}\right) -
f\!\left(x_0-\frac{\delta}{2}\right)}{\delta} + O(\delta^2).
\end{equation}
The ``big-Oh'' term represents all the terms that are not explicitly
shown and the value in parentheses, i.e., $\delta^2$, indicates the
lowest order of $\delta$ in these hidden terms. If $\delta$ is
sufficiently small, a reasonable approximation to the derivative may
be obtained by simply neglecting all the terms represented by the
``big-Oh'' term. Thus, the central-difference approximation is given
by
\begin{equation}
\left.\frac{df(x)}{dx}\right|_{x=x_0} \approx
\frac{f\!\left(x_0+\frac{\delta}{2}\right) -
f\!\left(x_0-\frac{\delta}{2}\right)}{\delta}.
\end{equation}
Note that the central difference provides an approximation of the
derivative of the function at $x_0$, but the function is not actually
sampled there. Instead, the function is sampled at the neighboring
points $x_0+\delta/2$ and $x_0-\delta/2$. Since the lowest power of
$\delta$ being ignored is second order, the central difference is said
to have second-order accuracy or second-order behavior. This implies
that if $\delta$ is reduced by a factor of $10$, the error in the
approximation should be reduced by a factor of $100$ (at least
approximately). In the limit as $\delta$ goes to zero, the
approximation becomes exact.
One can construct higher-order central differences. In order to get
higher-order behavior, more terms, i.e., more sample points, must be
used. Appendix \ref{ap:diff} presents the construction of a
fourth-order central difference. The use of higher-order central
differences in FDTD schemes is certainly possible, but there are some
complications which arise because of the increased ``stencil'' of the
difference operator. For example, when a PEC is present, it is
possible that the difference operator will extend into the PEC
prematurely or it may extend to the other side of a PEC sheet.
Because of these types of issues, we will only consider the use of
second-order central difference.
\section{The Yee Algorithm}
The FDTD algorithm as first proposed by Kane Yee in 1966 employs
second-order central differences. The algorithm can be summarized as
follows:
\begin{enumerate}
\item Replace all the derivatives in Ampere's and Faraday's laws with
finite differences. Discretize space and time so that the electric and
magnetic fields are staggered in both space and time.
\item Solve the resulting difference equations to obtain ``update
equations'' that express the (unknown) future fields in terms of
(known) past fields.
\item Evaluate the magnetic fields one time-step into the future so
they are now known (effectively they become past fields).
\item Evaluate the electric fields one time-step into the future so
they are now known (effectively they become past fields).
\item Repeat the previous two steps until the fields have been
obtained over the desired duration.
\end{enumerate}
At this stage, the summary is probably a bit too abstract. One really
needs an example to demonstrate the simplicity of the method.
However, developing the full set of three-dimensional equations would
be overkill and thus the algorithm will first be presented in
one-dimension. As you will see, the extension to higher dimensions is
quite simple.
\section{Update Equations in 1D \label{sec:update}}
Consider a one-dimensional space where there are only variations in
the $x$ direction. Assume that the electric field only has a $z$
component. In this case Faraday's law can be written
\begin{equation}
-\mu \frac{\partial \Hvec}{\partial t} =
\nabla \times \Evec =
\left|
\begin{array}{ccc}
\unitvec{x} & \unitvec{y} & \unitvec{z} \\
\frac{\partial}{\partial x} & 0 & 0 \\
0 & 0 & E_z
\end{array}
\right|
= -\unitvec{y}\frac{\partial E_z}{\partial x}.
\label{eq:faradayVec}
\end{equation}
Thus $H_y$ must be the only non-zero component of the magnetic field
which is time varying. (Since the right-hand side of this equation
has only a $y$ component, the magnetic field may have non-zero
components in the $x$ and $z$ directions, but they must be static. We
will not be concerned with static fields here.) Knowing this,
Ampere's law can be written
\begin{equation}
\epsilon \frac{\partial \Evec}{\partial t} =
\nabla \times \Hvec =
\left|
\begin{array}{ccc}
\unitvec{x} & \unitvec{y} & \unitvec{z} \\
\frac{\partial}{\partial x} & 0 & 0 \\
0 & H_y & 0
\end{array}
\right|
= \unitvec{z}\frac{\partial H_y}{\partial x}.
\label{eq:ampereVec}
\end{equation}
The two scalar equations obtained from \refeq{eq:faradayVec} and
\refeq{eq:ampereVec} are
\begin{eqnarray}
\mu \frac{\partial H_y}{\partial t} &=&
\frac{\partial E_z}{\partial x}, \label{eq:faradayScalar}
\\
\epsilon \frac{\partial E_z}{\partial t} &=&
\frac{\partial H_y}{\partial x}. \label{eq:ampereScalar}
\end{eqnarray}
The first equation gives the temporal derivative of the magnetic field
in terms of the spatial derivative of the electric field. Conversely,
the second equation gives the temporal derivative of the electric
field in terms of the spatial derivative of the magnetic field. As
will be shown, the first equation will be used to advance the magnetic
field in time while the second will be used to advance the electric
field. A method in which one field is advanced and then the other,
and then the process is repeated, is known as a leap-frog method.
The next step is to replace the derivatives in
\refeq{eq:faradayScalar} and \refeq{eq:ampereScalar} with finite
differences. To do this, space and time need to be discretized. The
following notation will be used to indicate the location where the
fields are sampled in space and time
\begin{eqnarray}
E_z(x,t) \!&=&\! E_z(m\Delx, q\Delt) = \fdtd{E_z}{m}{q}, \\
H_y(x,t) \!&=&\! H_y(m\Delx, q\Delt) = \fdtd{H_y}{m}{q},
\end{eqnarray}
where $\Delx$ is the spatial offset between sample points and $\Delt$
is the temporal offset. The index $m$ corresponds to the spatial
step, effectively the spatial location, while the index $q$
corresponds to the temporal step. When written as a superscript $q$
still represents the temporal step---it is not an exponent. When
implementing FDTD algorithms we will see that the spatial indices are
used as array indices while the temporal index, which is essentially a
global parameter, is not explicitly specified for each field location.
Hence, it is reasonable to keep the spatial indices as an explicit
argument while indicating the temporal index separately.
Although we only have one spatial dimension, time can be thought of as
another dimension. Thus this is effectively a form of two-dimensional
problem. The question now is: How should the electric and magnetic
field sample points, also known as nodes, be arranged in space and
time? The answer is shown in Fig.\ \ref{fig:spaceTime}.
\begin{figure}
\begin{center}
\epsfig{width=4.6in,file=Figures/Fdtd-intro/space-time-fdtd-update-new.eps}
\end{center}
\caption{The arrangement of electric- and magnetic-field nodes in
space and time. The electric-field nodes are shown as circles and
the magnetic-field nodes as triangles. The indicated point is where the
difference equation is expanded to obtain an update equation for
$H_y$.}
\label{fig:spaceTime}
\end{figure}
The electric-field nodes are shown as circles and the magnetic-field
nodes as triangles. Assume that all the fields below the dashed line
are known---they are considered to be in the past---while the fields
above the dashed line are future fields and hence unknown. The FDTD
algorithm provides a way to obtain the future fields from the past
fields.
As indicated in Fig.\ \ref{fig:spaceTime}, consider Faraday's
law at the space-time point $((m+1/2)\Delx,q\Delt)$
\begin{equation}
\left.\mu
\frac{\partial H_y}{\partial t}\right|_{(m+1/2)\Delx,q\Delt}
=
\left.\frac{\partial E_z}{\partial x}\right|_{(m+1/2)\Delx,q\Delt}.
\label{eq:faradayDiscrete}
\end{equation}
The temporal derivative is replaced by a finite difference involving
$\fdtdh{H_y}{m+\half}{q+\half}$ and $\fdtdh{H_y}{m+\half}{q-\half}$
(i.e., the magnetic field at a fixed location but two different times)
while the spatial derivative is replaced by a finite difference
involving $\fdtd{E_z}{m+1}{q}$ and $\fdtd{E_z}{m}{q}$ (i.e., the
electric field at two different locations but one time). This yields
\begin{equation}
\mu\frac{\fdtdh{H_y}{m+\half}{q+\half} -
\fdtdh{H_y}{m+\half}{q-\half}}{\Delt} =
\frac{\fdtd{E_z}{m+1}{q} - \fdtd{E_z}{m}{q}}{\Delx}.
\label{eq:faradayFdtd1D}
\end{equation}
Solving this for $\fdtdh{H_y}{m+\half}{q+\half}$ yields
\index{update equation!one-dimensional|(}
\begin{equation}
\fdtdh{H_y}{m+\half}{q+\half} = \fdtdh{H_y}{m+\half}{q-\half} +
\frac{\Delt}{\mu\Delx}
\left(\fdtd{E_z}{m+1}{q} - \fdtd{E_z}{m}{q}\right).
\label{eq:updateHy}
\end{equation}
This is known as an update equation, specifically the update equation
for the $H_y$ field. It is a generic equation which can be applied to
any magnetic-field node. It shows that the future value of $H_y$
depends on only its previous value and the neighboring electric
fields. After applying \refeq{eq:updateHy} to all the magnetic-field
nodes, the dividing line between future and past values has advanced a
half time-step. The space-time grid thus appears as shown in Fig.\
\ref{fig:spaceTimeUpdate} which is identical to Fig.\
\ref{fig:spaceTime} except for the advancement of the past/future
dividing line.
Now consider Ampere's law \refeq{eq:ampereScalar} applied at the
space-time point $(m\Delx,(q+1/2)\Delt)$ which is indicated in Fig.\
\ref{fig:spaceTimeUpdate}:
\begin{figure}
\begin{center}
\epsfig{width=4.6in,file=Figures/Fdtd-intro/space-time-fdtd-new.eps}
\end{center}
\caption{Space-time after updating the magnetic field. The dividing
line between future and past values has moved forward a half
temporal step. The indicated point is where the difference equation
is written to obtain an update equation for $E_z$.}
\label{fig:spaceTimeUpdate}
\end{figure}
\begin{equation}
\left.\epsilon
\frac{\partial E_z}{\partial t}\right|_{m\Delx,(q+1/2)\Delt}
=
\left.\frac{\partial H_y}{\partial x}\right|_{m\Delx,(q+1/2)\Delt}.
\end{equation}
Replacing the temporal derivative on the left with a finite
difference involving $\fdtd{E_z}{m}{q+1}$ and $\fdtd{E_z}{m}{q}$
and replacing the spatial derivative on the right with a finite
difference involving $\fdtdh{H_y}{m+\half}{q+\half}$ and
$\fdtdh{H_y}{m-\half}{q+\half}$ yields
\begin{equation}
\epsilon\frac{\fdtd{E_z}{m}{q+1} - \fdtd{E_z}{m}{q}}{\Delt} =
\frac{\fdtdh{H_y}{m+\half}{q+\half} - \fdtdh{H_y}{m-\half}{q+\half}}{\Delx}.
\label{eq:ampereFdtd1D}
\end{equation}
Solving for $\fdtd{E_z}{m}{q+1}$ yields
\begin{equation}
\fdtd{E_z}{m}{q+1} = \fdtd{E_z}{m}{q} +
\frac{\Delt}{\epsilon\Delx}
\left(\fdtdh{H_y}{m+\half}{q+\half} - \fdtdh{H_y}{m-\half}{q+\half}\right).
\label{eq:updateEz}
\end{equation}
Equation \refeq{eq:updateEz} is the update equation for the $E_z$
field. The indices in this equation are generic so that the same
equation holds for every $E_z$ node. Similar to the update equation for
the magnetic field, here we see that the future value of $E_z$ depends
on only its past value and the value of the neighboring magnetic
fields.
\index{update equation!one-dimensional|)}
After applying \refeq{eq:updateEz} to every electric-field node in the
grid, the dividing line between what is known and what is unknown
moves forward another one-half temporal step. One is essentially back
to the situation depicted in Fig.\ \ref{fig:spaceTime}---the future
fields closest to the dividing line between the future and past are
magnetics fields. They would be updated again, then the electric
fields would be updated, and so on.
It is often convenient to represent the update coefficients
$\Delt/\epsilon\Delx$ and $\Delt/\mu\Delx$ in terms of the ratio of
how far energy can propagate in a single temporal step to the spatial
step. The maximum speed electromagnetic energy can travel is the
speed of light in free space $c=1/\sqrt{\epsilon_0\mu_0}$ and hence
the maximum distance energy can travel in one time step is $c\Delt$
(in all the remaining discussions the symbol $c$ will be reserved for
the speed of light in free space). The ratio $c\Delt/\Delx$ is often
called the Courant number \index{Courant number!definition} which we
label $S_c$. It plays an important role in determining the stability
of a simulation (hence the use of $S$) and will be considered further
later. Letting $\mu=\mu_r\mu_0$ and $\epsilon=\epsilon_r\epsilon_0$,
the coefficients in \refeq{eq:updateEz} and \refeq{eq:updateHy} can be
written
\begin{eqnarray}
\frac{1}{\epsilon} \frac{\Delt}{\Delx} \!&=&\!
\frac{1}{\epsilon_r\epsilon_0}
\frac{\sqrt{\epsilon_0\mu_0}}{\sqrt{\epsilon_0\mu_0}}
\frac{\Delt}{\Delx} =
\frac{\sqrt{\epsilon_0\mu_0}}{\epsilon_r\epsilon_0}
\frac{c\Delt}{\Delx} =
\frac{1}{\epsilon_r}
\sqrt{\frac{\mu_0}{\epsilon_0}}
\frac{c\Delt}{\Delx} =
\frac{\eta_0}{\epsilon_r}
\frac{c\Delt}{\Delx} =
\frac{\eta_0}{\epsilon_r} S_c
\label{eq:coefEz} \\
\frac{1}{\mu} \frac{\Delt}{\Delx} \!&=&\!
\frac{1}{\mu_r\mu_0}
\frac{\sqrt{\epsilon_0\mu_0}}{\sqrt{\epsilon_0\mu_0}}
\frac{\Delt}{\Delx} =
\frac{\sqrt{\epsilon_0\mu_0}}{\mu_r\mu_0}
\frac{c\Delt}{\Delx} =
\frac{1}{\mu_r}
\sqrt{\frac{\epsilon_0}{\mu_0}}
\frac{c\Delt}{\Delx} =
\frac{1}{\mu_r\eta_0}
\frac{c\Delt}{\Delx} =
\frac{1}{\mu_r\eta_0} S_c
\label{eq:coefHy}
\end{eqnarray}
where $\eta_0=\sqrt{\mu_0/\epsilon_0}$ is the characteristic impedance
of free space.
In FDTD simulations there are restrictions on how large a temporal
step can be. If it is too large, the algorithm produces unstable
results (i.e., the numbers obtained are completely meaningless and
generally tend quickly to infinity). At this stage we will not
consider a rigorous analysis of stability. However, thinking about
the way fields propagate in an FDTD grid, it seems logical that energy
should not be able to propagate any further than one spatial step for
each temporal step, i.e., $c\Delt\le\Delx$. This is because in the
FDTD algorithm each node only affects its nearest neighbors. In one
complete cycle of updating the fields, the furthest a disturbance could
propagate is one spatial step. It turns out that the optimum ratio
for the Courant number (in terms of minimizing numeric errors)
is also the maximum ratio. Hence, for the one-dimensional simulations
considered initially, we will use
\begin{equation}
S_c = \frac{c\Delt}{\Delx} = 1.
\end{equation}
When first obtaining the update equations for the FDTD algorithm, it
is helpful to think in terms of space-time. However, treating time as
an additional dimension can be awkward. Thus, in most situations it
is more convenient to think in terms of a single spatial dimension
where the electric and magnetic fields are offset a half spatial step
from each other. This is depicted in Fig.\
\ref{fig:fdtdOneD}. The temporal offset between the electric and
magnetic field is always understood whether explicitly shown or not.
\begin{figure}
\begin{center}
\epsfig{width=5.0in,file=Figures/Fdtd-intro/space-fdtd-1d.eps}
\end{center}
\caption{A one-dimensional FDTD space showing the spatial offset
between the magnetic and electric fields.}
\label{fig:fdtdOneD}
\end{figure}
\section{Computer Implementation of a One-Dimensional\\ FDTD Simulation}
Our goal now is to translate the update equations \refeq{eq:updateHy}
and \refeq{eq:updateEz} into a usable computer program. The first
step is to discard, at least to a certain extent, the
superscripts---time is a global parameter and will be recorded in a
single integer variable. Time is not something about which each node
needs to be concerned.
Next, keep in mind that in most computer languages the equal sign is
used as ``the assignment operator.'' In C, the following is a
perfectly valid statement
\begin{verbatim}
a = a+b;
\end{verbatim}
In the usual mathematical sense, this statement is only true if $b$
were zero. However, to a computer this statement means take the value
of $b$, add it to the old value of $a$, and place the result back in
the variable $a$. Essentially we are updating the value of $a$. In C
this statement can be written more tersely as
\begin{verbatim}
a += b;
\end{verbatim}
When writing a computer program to implement the FDTD algorithm, one
does not bother trying to construct a program that explicitly uses
offsets of one-half. Nodes are stored in arrays and, as is standard
practice, individual array elements are specified with integer
indices. Thus, the computer program (or, perhaps more correctly, the
{\em author} of the computer program) implicitly incorporates the fact
that electric and magnetic fields are offset while using only integer
indices to specify location. As you will see, spatial location and
the array index will be virtually synonymous.
For example, assume two arrays, {\tt ez} and {\tt hy}, are
declared which will contain the $E_z$ and $H_y$ fields at 200 nodes
\begin{verbatim}
double ez[200], hy[200], imp0=377.0;
\end{verbatim}
The variable {\tt imp0} is the characteristic impedance of free space
and will be used in the following discussion (it is initialized to a
value of $377.0$ in this declaration). One should think of the
elements in the {\tt ez} and {\tt hy} arrays as being offset from
each other by a half spatial step even though the array values will be
accessed using an integer index.
It is arbitrary whether one initially wishes to think of an {\tt ez}
array element as existing to the right or the left of an {\tt hy}
element with the same index (we assume ``left'' corresponds to
descreasing values of $x$ while ``right'' corresponds to increasing
values). Here we will assume {\tt ez} nodes are to the left of {\tt
hy} nodes with the same index. This is illustrated in Fig.\
\ref{fig:oneDArrays} where {\tt ez[0]} is to the left of {\tt hy[0]},
{\tt ez[1]} is to the left of {\tt hy[1]}, and so on. In general,
when a Courier font is used, e.g., {\tt hy[m]}, we are considering an
array and any offsets of one-half associated with that array are
implicitly understood. When Times-Italic font is use, e.g.,
$\fdtdh{H_y}{m+\half}{q+\half}$ we are discussing the field itself and
offsets will be given explicitly.
\begin{figure}
\begin{center}
\epsfig{width=5.0in,file=Figures/Fdtd-intro/fdtd-1d-computer.eps}
\end{center}
\caption{A one-dimensional FDTD space showing the assumed spatial
arrangement of the electric- and magnetic-field nodes in the arrays
{\tt ez} and {\tt hy}. Note that an electric-field node is assumed
to exist to the left of the magnetic-field node with the same
index.} \label{fig:oneDArrays}
\end{figure}
Assuming a Courant number of unity ($S_c=1$), the node {\tt hy[1]}
could be updated with a statement such as
\begin{verbatim}
hy[1] = hy[1] + (ez[2] - ez[1]) / imp0;
\end{verbatim}
In general, any magnetic-field node can be updated with
\begin{verbatim}
hy[m] = hy[m] + (ez[m + 1] - ez[m]) / imp0;
\end{verbatim}
For the electric-field nodes, the update equation can be written
\begin{verbatim}
ez[m] = ez[m] + (hy[m] - hy[m - 1]) * imp0;
\end{verbatim}
These two update equations, placed in appropriate loops, are the
engines that drive an FDTD simulation. However, there are a few
obvious pieces missing from the puzzle before a useful simulation can
be performed. These missing pieces include
\begin{enumerate}
\item Nodes at the end of the physical space do not have neighboring
nodes to one side. For example, there is no {\tt hy[-1]} node for
the {\tt ez[0]} node to use in its update equation. Similarly, if
the arrays are declared with 200 element, there is no {\tt ez[200]}
available for {\tt hy[199]} to use in its update equation (recall
that the index of the last element in a C array is one less than the
total number of elements---the array index represents the offset
from the first element of the array). Therefore a standard update
equation cannot be used at these nodes.
\item Only a constant impedance is used so only a homogeneous medium
can be modeled (in this case free space).
\item As of yet there is no energy present in the field. If the
fields are initially zero, they will remain zero forever.
\end{enumerate}
The first issue can be addressed using absorbing boundary conditions
(ABC's). There are numerous implementations one can use. In later
material we will be consider only a few of the more popular
techniques.
The second restriction can be removed by allowing the permittivity and
permeability to change from node to node. However, in the interest of
simplicity, we will continue to use a constant impedance for a little
while longer.
The third problem can be overcome by initializing the fields to a
non-zero state. However, this is cumbersome and typically not a good
approach. Better solutions are to introduce energy via either a
hardwired source, an additive source, or a total-field/scattered-field
(TFSF) boundary. We will consider implementation of each of these
approaches.
\section{Bare-Bones Simulation \label{sec:bareBones}}
Let us consider a simulation of a wave propagating in free space where
there are 200 electric- and magnetic-field nodes. The code is shown
in Program \ref{pro:1DbareBones}.
\begin{program}
{\tt 1DbareBones.c}: \index{1DbareBones.c@{\tt 1DbareBones.c}}
Bare-bones one-dimensional simulation with a hard
source. \label{pro:1DbareBones}
\codemiddle
\begin{lstlisting}
/* Bare-bones 1D FDTD simulation with a hard source. */
#include <stdio.h>
#include <math.h>
#define SIZE 200
int main()
{
double ez[SIZE] = {0.}, hy[SIZE] = {0.}, imp0 = 377.0; /*@ \label{bareBonesA} @*/
int qTime, maxTime = 250, mm; /*@ \label{bareBonesB} @*/
/* do time stepping */
for (qTime = 0; qTime < maxTime; qTime++) { /*@ \label{bareBonesC} @*/
/* update magnetic field */
for (mm = 0; mm < SIZE - 1; mm++) /*@ \label{bareBonesD} @*/
hy[mm] = hy[mm] + (ez[mm + 1] - ez[mm]) / imp0;
/* update electric field */
for (mm = 1; mm < SIZE; mm++) /*@ \label{bareBonesE} @*/
ez[mm] = ez[mm] + (hy[mm] - hy[mm - 1]) * imp0;
/* hardwire a source node */
ez[0] = exp(-(qTime - 30.) * (qTime - 30.) / 100.); /*@ \label{bareBonesF} @*/
printf("%g\n", ez[50]); /*@ \label{bareBonesG} @*/
} /* end of time-stepping */
return 0;
}
\end{lstlisting}
\end{program}
In the declaration of the field arrays in line \ref{bareBonesA},
``{\tt=\{0.\}}'' has been added to ensure that these arrays are
initialized to zero. (For larger arrays this is not an efficient
approach for initializing the arrays and we will address this fact
later.) The variable {\tt qTime} is an integer counter that serves as
the temporal index or time step. The total number of time steps in
the simulation is dictated by the variable {\tt maxTime} which is set
to $250$ in line \ref{bareBonesB} ($250$ was chosen arbitrarily---it
can be any value desired).
Time-stepping is accomplished with the for-loop that begins on line
\ref{bareBonesC}. Embedded within this time-stepping loop are two
additional (spatial) loops---one to update the magnetic field and the
other to update the electric field. The magnetic-field update loop
starting on line \ref{bareBonesD} excludes the last magnetic-field
node in the array, {\tt hy[199]}, since this node lacks one
neighboring electric field. For now we will leave this node zero.
The electric-field update loop in line \ref{bareBonesE} starts with a
spatial index {\tt m} of $1$, i.e., it does not include {\tt ez[0]}
which is the first $E_z$ node in the grid. The value of {\tt ez[0]}
is dictated by line \ref{bareBonesF} which is a Gaussian function that
will have a maximum value of unity when the time counter {\tt qTime}
is $30$. The first time through the loop, when {\tt qTime} is zero,
{\tt ez[0]} will be set to $\exp(-9)\approx 1.2341\times 10^{-4}$
which is small relative to the maximum value of the source. Line
\ref{bareBonesG} prints the value of {\tt ez[50]} to the screen, once
for each time step. A plot of the output generated by this program is
shown in Fig.\
\ref{fig:bareBones}.
\begin{figure}
\begin{center}
\epsfig{width=4.6in,file=Code/Fdtd-intro/bare-bones-output200.eps}
\end{center}
\caption{Output generated by Program \ref{pro:1DbareBones}.}
\label{fig:bareBones}
\end{figure}
Note that the output is a Gaussian. The excitation is introduced at
{\tt ez[0]} but the field is recorded at {\tt ez[50]}. Because
$c\Delt=\Delx$ in this simulation (i.e., the Courant number is unity),
the field moves one spatial step for every time step. The separation
between the source point and the observation point results in the
observed signal being delayed by $50$ time steps from what it was at
the source. The source function has a peak at $30$ time steps but, as
can be seen from Fig.\ \ref{fig:bareBones}, the field at the
observation point is maximum at time step $80$.
Consider a slight modification to Program \ref{pro:1DbareBones} where
the simulation is run for $1000$ time steps instead of $250$ (i.e.,
{\tt maxTime} is set to $1000$ in line \ref{bareBonesB} instead of
$250$). The output obtained in this case is shown in Fig.\
\ref{fig:bareBonesLong}.
\begin{figure}
\begin{center}
\epsfig{width=4.6in,file=Code/Fdtd-intro/bare-bones-output1000.eps}
\end{center}
\caption{Output generated by Program \ref{pro:1DbareBones} but with
{\tt maxTime} set to $1000$.}
\label{fig:bareBonesLong}
\end{figure}
Why are there multiple peaks here and why are they both positive and
negative?
The last magnetic-field node in the grid is initially zero and remains
zero throughout the simulation. When the field encounters this node
it essentially see a perfect magnetic conductor (PMC). To satisfy the
boundary condition at this node, i.e., that the total magnetic field
go to zero, a reflected wave is created which reverses the sign of the
magnetic field but preserves the sign of the electric field. This
phenomenon is considered in more detail in the next section. The
second peak in Fig.\ \ref{fig:bareBonesLong} is this reflected wave.
The reflected wave continues to travel in the negative direction until
it encounters the first electric-field node {\tt ez[0]}. This node
has its value set by the source function and is oblivious to what is
happening in the interior of the grid. In this particular case, by
the time the reflected field reaches the left end of the grid, the
source function has essentially gone to zero and nothing is going to
change that. Thus the node {\tt ez[0]} behaves like a perfect
electric conductor (PEC). To satisfy the boundary conditions at this
node, the wave is again reflected, but this time the electric field
changes sign while the sign of the magnetic field is preserved. In
this way the field which was introduced into the grid continues to
bounce back and forth until the simulation is terminated. The
simulation is of a resonator with one PMC wall and one PEC wall.
(Note that the boundary condition at {\tt ez[0]} is the same whether
or not the source function has gone to zero. Any incoming field
cannot change the value at {\tt ez[0]} and hence a reflected wave must
be generated which has equal magnitude but opposite sign from the
incoming field.)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{PMC Boundary in One Dimension \label{sec:pmcBoundary}}
In Program \ref{pro:1DbareBones} one side of the grid (the ``right
side'') is terminated by a magnetic field which is always zero. It
was observed that this node acts as a perfect magnetic conductor (PMC)
which produces a reflected wave where the electric field is not
inverted while the magnetic field is inverted. To understand fully
why this is the case, let us consider the right side of a
one-dimensional domain where $200$ electric- and magnetic-field nodes
are used to model free space. Assume the Courant number is unity and
the impedance of free space is $377$. The last node in the grid is
{\tt hy[199]} and it will always remain zero. The other nodes in the
grid are updated using, in C notation:
\begin{eqnarray}
\mbox{\tt\ \ \ \ ez[m] = ez[m] + (hy[m] - hy[m - 1]) * 377;} \label{eq:ezUpdateC}\\
\mbox{\tt\ \ \ \ hy[m] = hy[m] + (ez[m + 1] - ez[m]) / 377;} \label{eq:hyUpdateC}
\end{eqnarray}
Assume that a Dirac delta pulse, i.e., a unit amplitude pulse existing
at a single electric-field node in space-time, is nearing the end of
the grid. Table \ref{tab:pmcBoundary} shows the fields at progressive
time-steps starting at a time $q$ when the pulse has reached node {\tt
ez[198]}.
\renewcommand{\multirowsetup}{\centering}
\newlength{\LL}\settowidth{\LL}{time}
\begin{table}
\begin{center}
\begin{tabular}{cl|cccccc}
\multirow{12}{\LL}{time\\step}& & \multicolumn{6}{c}{node} \\
&
& {\tt ez[197]} &{\tt hy[197]} &{\tt ez[198]}&
{\tt hy[198]} &{\tt ez[199]} &{\tt hy[199]}\\ \hline
& $q-1/2$& &$-1/377$ & &$0$ & &$0$\\
& $q$ & $0$ & &$1$ & &$0$ &\\ \cline{2-8}
& $q+1/2$& &$0$ & &$-1/377$ & &$0$\\
& $q+1$ & $0$ & &$0$ & &$1$ &\\ \cline{2-8}
& $q+3/2$& &$0$ & &$0$ & &$0$\\
& $q+2$ & $0$ & &$0$ & &$1$ &\\ \cline{2-8}
& $q+5/2$& &$0$ & &$1/377$ & &$0$\\
& $q+3$ & $0$ & &$1$ & &$0$ &\\ \cline{2-8}
& $q+5/2$& &$1/377$ & &$0$ & &$0$ \\
& $q+4$ & $1$ & &$0$ & &$0$ &
\end{tabular}
\end{center}
\caption{Electric- and magnetic-field nodes at the ``end'' of arrays
which have 200 elements, i.e., the last node is {\tt hy[199]} which is
always set to zero. A pulse of unit amplitude is propagating to the
right and has arrived at {\tt ez[198]} at time-step $q$. Time is
advancing as one reads down the columns.}
\label{tab:pmcBoundary}
\end{table}
At time $q$ node {\tt ez[198]} is unity while {\tt hy[197]} was set to
$-1/377$ at the previous update of the magnetic fields. When the
magnetic fields are updated at time $q+1/2$ the update equation
\refeq{eq:hyUpdateC} dictates that {\tt hy[197]} be set to zero (the
``old'' value of the magnetic field cancels the contribution from the
electric field). Meanwhile, {\tt hy[198]} becomes $-1/377$. All
other magnetic-field nodes will be zero.
Updating the electric field at time-step $q+1$ results in {\tt
ez[198]} being set to zero while {\tt ez[199]} is set to one---the
pulse advances one spatial step to the right. If the normal update
equation could be used at node {\tt hy[199]}, at time $q+3/2$ it would
be set to $-1/377$. However, because there is no neighboring electric
field to the right of {\tt hy[199]}, the update equation cannot be
used and, lacking an alternative way of calculating its value, {\tt
hy[199]} is left as zero. Thus at time $q+3/2$ {\em all} the
magnetic-field nodes in the grid are zero.
When the electric field is updated at time $q+2$ essentially nothing
happens. The electric fields are updated from their old values and
the difference of surrounding magnetic fields. However all magnetic
fields are zero. Thus the new electric field is the same as the old
electric field.
At time $q+5/2$ the unit pulse which exists at {\tt ez[199]} causes
{\tt hy[198]} to become $1/377$ which is the negative of what it was two
times steps ago. From this time forward, the pulse propagates back to
the left with the electric field maintaining unit amplitude.
This discussion is for a single pulse, but any incident field could be
treated as a string of pulses and then one would merely have to
superimpose their values. This dicussion further supposes the Courant
number is unity. When the Courant number is not unity the termination
of the grid still behaves as a PMC wall, but the pulse will not
propagate without distortion (it suffers dispersion because of the
properties of the grid itself as will be discussed in more detail in
Sec.\ \ref{sec:gridDispersion}).
If the grid were terminated on an electric-field node which was
always set to zero, that node would behave as a perfect electric
conductor. In that case the reflected electric field would have the
opposite sign from the incident field while the magnetic field would
preserve its sign. This is what happens to any field incident on the
left side of the grid in Program \ref{pro:1DbareBones}.
\section{Snapshots of the Field}
In Program \ref{pro:1DbareBones} the field at a single point is
recorded to a file. Alternatively, it is often useful to view the
fields over the entire computational domain at a single instant of
time, i.e., take a ``snapshot'' that shows the field throughout space.
Here we describe one way in which this can be conveniently implemented
in C.
The approach adopted here will open a separate file for each snapshot.
Each file will have a common base name, then a dot, and then a
sequence number which will be called the frame number. So, for
example, the files might be called {\tt sim.0}, {\tt sim.1}, {\tt
sim.2}, and so on. To accomplish this, the fragments shown in
Fragments \ref{frag:snapshot} and \ref{frag:snapshotI} would be added to a
program (such as Program \ref{pro:1DbareBones}).
\begin{fragment}
Declaration of variables associated with taking snapshots. The base
name is stored in the character array {\tt basename} and the
complete file name for each frame is stored in {\tt filename}. Here
the base name is initialized to {\tt sim} but, if desired, the user
could be prompted for the base name. The integer {\tt frame} is the
frame number for each snapshot and is initialized to
zero. \label{frag:snapshot}
\codemiddle
\begin{lstlisting}
char basename[80] = "sim", filename[100];
int frame = 0;
FILE *snapshot;
\end{lstlisting}
\end{fragment}
\begin{fragment}
Code to generate the snapshots. This would be placed inside the
time-stepping loop. The initial if statement ensures the electric
field is recorded every tenth time-step.\label{frag:snapshotI}
\codemiddle
\begin{lstlisting}
/* write snapshot if time-step is a multiple of 10 */
if (qTime % 10 == 0) { /*@ \label{fragSnapA} @*/
/* construct complete file name and increment frame counter */
sprintf(filename, "%s.%d", basename, frame++); /*@ \label{fragSnapB} @*/
/* open file */
snapshot = fopen(filename, "w"); /*@ \label{fragSnapC} @*/
/* write data to file */
for (mm = 0; mm < SIZE; mm++) /*@ \label{fragSnapD} @*/
fprintf(snapshot, "%g\n", ez[mm]);
/* close file */
fclose(snapshot); /*@ \label{fragSnapE} @*/
}
\end{lstlisting}
\end{fragment}
In Fragment \ref{frag:snapshot} the base name is initialized to {\tt
sim} but the user could be prompted for this. The integer variable
{\tt frame} is the frame (or snapshot) counter that will be
incremented each time a snapshot is taken. It is initialized to zero.
The file pointer {\tt snapshot} is used for the output files.
The code shown in Fragment \ref{frag:snapshotI} would be placed inside
the time-stepping loop of Program \ref{pro:1DbareBones}. Line
\ref{fragSnapA} checks, using the modulo operator ({\tt \%}) if the
time step is a multiple of $10$. ($10$ was chosen somewhat
arbitrarily. If snapshots were desired more frequently, a smaller
value would be used. If snapshots were desired less frequently, a
larger value would be used.) If the time step is a multiple of $10$,
the complete output-file name is constructed in line \ref{fragSnapB}
by writing the file name to the string variable {\tt filename}.
(Since zero is a multiple of $10$, the first snapshot that is taken
corresponds to the fields at time zero. This data would be written to
the file {\tt sim.0}. Note that in Line \ref{fragSnapB} the frame
number is incremented each time a file name is created. The file is
opened in line \ref{fragSnapC} and the data is written using the loop
starting in line \ref{fragSnapD}. Finally, the file is closed in line
\ref{fragSnapE}.
Fig.\ \ref{fig:snapshots} shows the snapshots of the field at time
steps $20$, $30$, and $40$ using essentially the same code as Program
\ref{pro:1DbareBones}---the only difference being the addition of the
code to take the snapshots. The corresponding files are {\tt sim.2},
{\tt sim.3}, and {\tt sim.4}. In these snapshots the field can be
seen entering the computational domain from the left and propagating
to the right.
\begin{figure}
\begin{center}
\epsfig{width=3.5in,file=Code/Fdtd-intro/snapshot1.eps}
\epsfig{width=3.5in,file=Code/Fdtd-intro/snapshot2.eps}
\epsfig{width=3.5in,file=Code/Fdtd-intro/snapshot3.eps}
\end{center} \caption{Snapshots taken at time-steps $20$, $30$, and
$40$ of the $E_z$ field generated by Program \ref{pro:1DbareBones}.
The field is seen to be propagating away from the hardwired source
at the left end of the grid.} \label{fig:snapshots}
\end{figure}
\section{Additive Source \label{sec:additive}}
Hardwiring the source, as was done in Program \ref{pro:1DbareBones},
has the severe shortcoming that no energy can pass through the source
node. This problem can be rectified by using an additive source.
Consider Ampere's law with the current density term:
\begin{equation}
\nabla\times\Hvec = \Jvec +
\epsilon\frac{\partial \Evec}{\partial t}.
\label{eq:ampereWithCurrent}
\end{equation}
The current density $\Jvec$ can represent both the conduction current
due to flow of charge in a material under the influence of the
electric field, i.e., current given by $\sigma\Evec$, as well as the
current associated with any source, i.e., an ``impressed current.''
At this point we are just interested in the source aspect of $\Jvec$
and will return to the issue of finite conductivity in Sec.\
\ref{sec:loss} and Sec.\ \ref{sec:conductivity}. Rearranging
\refeq{eq:ampereWithCurrent} slightly yields
\begin{equation}
\frac{\partial \Evec}{\partial t} =
\frac{1}{\epsilon} \nabla\times\Hvec - \frac{1}{\epsilon}\Jvec.
\label{eq:ampereTweak}
\end{equation}
This equation gives the temporal derivative of the electric field in
terms of the spatial derivative of the magnetic field---which is as
before---and an additional term which can be thought of as the forcing
function for the system. This current can be specified to be whatever
is desired.
To translate \refeq{eq:ampereTweak} into a form suitable for the FDTD
algorithm, the spatial derivatives are again expressed in terms of
finite differences and then one solves for the future fields in terms
of past fields. Recall that for Ampere's law, the update equation for
$\fdtd{E_z}{m}{q}$ was obtained by applying finite differences at the
space-time point $(m\Delx,(q+1/2)\Delt)$. Going through the exact
same procedure but adding the source term yields
\begin{equation}
\fdtd{E_z}{m}{q+1} = \fdtd{E_z}{m}{q} +
\frac{\Delt}{\epsilon\Delx}
\left(\fdtdh{H_y}{m+\half}{q+\half} - \fdtdh{H_y}{m-\half}{q+\half}\right)
- \frac{\Delt}{\epsilon}\fdtd{J_z}{m}{q+\half}.
\label{eq:updateEzSource}
\end{equation}
The source current could potentially be distributed over a number of
nodes, but for the sake of introducing energy to the grid, it suffices
to apply it to a single node.
In order to preserve the original update equation (which is sometimes
handy when writing loops), \refeq{eq:updateEzSource} can be separated
into two steps: first the usual update is applied and then the source
term is added. For example:
\begin{eqnarray}
\fdtd{E_z}{m}{q+1} &=& \fdtd{E_z}{m}{q} + \frac{\Delt}{\epsilon\Delx}
\left(\fdtdh{H_y}{m+\half}{q+\half} - \fdtdh{H_y}{m-\half}{q+\half}\right) \\
\fdtd{E_z}{m}{q+1} &=& \fdtd{E_z}{m}{q+1} -
\frac{\Delt}{\epsilon}\fdtd{J_z}{m}{q+\half}. \label{eq:addCurrent1D}
\end{eqnarray}
In practice the source current might only exist at a single node in
the 1D grid (as will be the case in the examples to come). Thus,
\refeq{eq:addCurrent1D} would be applied only at the node where the
source current is non-zero.
Generally the amplitude and the sign of the source function are not a
concern. When calculating things such as the scattering cross-section
or the reflection coefficient, one always normalizes by the incident
field. Therefore we do not need to specify explicitly the value of
$\Delt/\epsilon$ in \refeq{eq:addCurrent1D}---it suffices to merely
treat this coefficient as being contained in the source function
itself.
A program that implements an additive source and takes snapshots of
the electric field is shown in Program \ref{pro:1Dadditive}. The
changes from Program \ref{pro:1DbareBones} are shown in bold. The
source function is exactly the same as before except now, instead of
setting the value of {\tt ez[0]} to the value of this function, the
source function is added to {\tt ez[50]}. The source is introduced in
line \ref{1DadditiveE} and the update equations are unchanged from
before. (Note that in this chapter the programs will be somewhat
verbose, simplistic, and repetitive. Once we are comfortable with the
FDTD algorithm we will pay more attention to better coding practices.)
\begin{program}
{\tt 1Dadditive.c}: \index{1Dadditive.c@{\tt 1Dadditive.c}}
One-dimensional FDTD program with an additive
source. \label{pro:1Dadditive}
\codemiddle
\begin{lstlisting}
/* 1D FDTD simulation with an additive source. */
#include <stdio.h>
#include <math.h>
#define SIZE 200
int main()
{
double ez[SIZE] = {0.}, hy[SIZE] = {0.}, imp0 = 377.0;
int qTime, maxTime = 200, mm;
/*b*/
char basename[80] = "sim", filename[100];
int frame = 0;
FILE *snapshot;
/*n*/
/* do time stepping */
for (qTime = 0; qTime < maxTime; qTime++) {
/*@ \label{1DadditiveA} @*/
/* update magnetic field */ /*@ \label{1DadditiveB} @*/
for (mm = 0; mm < SIZE - 1; mm++)
hy[mm] = hy[mm] + (ez[mm + 1] - ez[mm]) / imp0;
/*@ \label{1DadditiveC} @*/
/* update electric field */ /*@ \label{1DadditiveD} @*/
for (mm = 1; mm < SIZE; mm++)
ez[mm] = ez[mm] + (hy[mm] - hy[mm - 1]) * imp0;
/*b*/
/* use additive source at node 50 */
ez[50] += exp(-(qTime - 30.) * (qTime - 30.) / 100.); /*@ \label{1DadditiveE} @*/
/* write snapshot if time a multiple of 10 */
if (qTime % 10 == 0) {
sprintf(filename, "%s.%d", basename, frame++);
snapshot=fopen(filename, "w");
for (mm = 0; mm < SIZE; mm++)
fprintf(snapshot, "%g\n", ez[mm]);
fclose(snapshot);
}/*n*/
} /* end of time-stepping */
return 0;
}
\end{lstlisting}
\end{program}
Snapshots of $E_z$ taken at time-steps $20$, $30$, and $40$ are shown
in Fig.\ \ref{fig:additive}. Note that the field originates from node
$50$ and that it propagates to either side of this node. Also notice
that the peak amplitude is half of what it was when the source
function was implemented as a hardwired source.
\begin{figure}
\begin{center}
\epsfig{width=3.5in,file=Code/Fdtd-intro/additive1.eps}
\epsfig{width=3.5in,file=Code/Fdtd-intro/additive2.eps}