pcb-rnd knowledge pool

 

direct path to openems for s-parameters

s_param by Evan Foss on 2019-04-19

Tags: howto, fem, openems, sim, simulation, hf, microwave, s-parameter

node source

 

 

Abstract: A quick introduction to solving for s-parameters - one of the most basic uses of pcb-rnd's direct interface to openems. This includes selecting ports, generating mesh, exporting, processing, and visualization.

 

0. Dependencies

This pool entry requires a working install of the following:

1. Example Use Case

The geometry for this test case is provided by Koen De Vleeschauwer who created this geometry originally for his hyp2mat project, which imports hyperlynx format to openEMS.

The following image is of the pcb layout Koen created after import to pcb-rnd. The board file is available here with the minor tweeks described for ports.

This is a hairpin filter. The two grey squares on it are padstacks set up to act as ports for openEMS to measure from. In abstract this is no different from how a VNA (Vector Network Analyzer) is used. Select the ports, a few parameters like frequency of interest, and then the machine graphs the data. While hyp2mat required the user to add large blocks of code for meshing, ports, materials, etc, all of these are now resolved in pcb-rnd. Only post processing (which usually means graphing) requires the user to touch the code.

2. Setup

Setup follows these steps:

To save the trouble of exporting the files, copying this text, and etc the final files are provided here:

2a. Configuring the Ports

While openEMS supports a few port types, we only use 1D ports to simplify mapping over from pcb-rnds model. There are a few attributes we will be using on these ports. They are the following:

The following image shows the attributes of the left port in pcb-rnd property editor:

As shown above, the left port is vertical and the source in this simulation. It has a 50 Ohm impedance.

The attributes of the right port are shown below:

2b. Selecting the Excitation

This example uses Gaussian excitation. It can be selected from the dialog shown below, which is invoked with the openemsexcitation() action:

[ for the uninitiated this is pressing the ':' key then typing openemsexcitation ]

These names are designed to match the ones used in openEMS itself for transparency. They are documented here.

2c. Meshing

Meshing is going to require a pool entry of its own to just explain rules of thumb. For now these values will suffice. Keep in mind these mesh settings and other parameters were picked to give a reasonable simulation speed for exploring the tool chain and process and not for an ideal output precision.

Invoking the action mesh() from the pcb-rnd command line will bring up the following window:

To make it easier to follow along the mesh values can be loaded from this file.

XY-Mesh

This is what the resulting mesh looks like for the XY axis, after clicking the Generate mesh! button:

Z-Mesh

Z-Mesh is mostly straightforward. In the interest of accelerating the simulation we made a compromise. To save computational resources openEMS has a way of emulating thickness on conductive layers. This saves adding density to the mesh in the Z axis.

This is what the resulting mesh looks like for the Z axis:

Boundary

Boundary Conditions are in this case the material the model is boxed in. For this example we are assuming the pcb is in a theoretically ideal case. That ideal case is a box made of a material with a perfectly matched impedance.

For descriptions of the boundary conditions openEMS supports see the openEMS documentation here.

Mesh I/O

2d. Exporting

Invoked as a normal direct export (File menu, export layout, select openEMS on the left).

The export dialog has a lot of options but they are a lot simpler to deal with than the mesh.

For more a detailed explanation of the terms epsilon, mue, kappa, and sigma please see the corresponding openems documentation here .

The following picture is what this looks like:

3. Post Processing (Graphing)

Post processing is a dense topic and this how-to is intended to demonstrate the general idea so it intentionally lacks details on alternative configurations for the following. There are way more advanced post processing options including the use of things like Paraview to do animations of wave propogation in 3 dimensional space. For S-Parameters what is typically more desirable is the more pedestrian 2D plot of S11 and S21. Because of the open-ended nature of this subject, pcb-rnd does not pretend to provide a post processing interface. The following code is just meant to be placed or called from the exported files pcb-rnd already produced. All of this is to say the user needs to add some code to the .sim.m file in this case hairpinfilter.graph.m and have it run after the end of the simulation.


echo "run hairpinfilter.graph.m" >> hairpinfilter.sim.m

For this example the contents of hairpinfilter.graph.m is listed below. This code is written in matlab but like the rest of the code in openEMS and this project it will work in octave as well.


CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); 
% include the line above to launch CSXCAD omitt it to avoid launching CSXCAD
RunOpenEMS( Sim_Path, Sim_CSX );

%% post-processing
close all
f = linspace( 1e6, 2e9, 1601 );
port = calcPort( port, Sim_Path, f, 'RefImpedance', 50);

s11 = port{1}.uf.ref./ port{1}.uf.inc;
s21 = port{2}.uf.ref./ port{1}.uf.inc;

plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2);
hold on;
grid on;
plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2);
legend('S_{11}','S_{21}');
ylabel('S-Parameter (dB)','FontSize',12);
xlabel('frequency (GHz) \rightarrow','FontSize',12);
ylim([-60 2]);
print ('hairpinfilter_simulation.png', '-dpng');

The only parts of this which are openEMS specific are the references to the ports. For more on the subject of ports, please see the openEMS documentation here under "Frequency Domain Data".

4. Execution

This is trivial in comparison to the other steps. Execution is included as its own step to share what the output of this example looks like when everything runs correctly. Not to scare potential users, but openEMS execution times can be long and resource usage will probably be high. This is true for all FDTD solvers.

Partway through the following command line output CSXCAD will be launched for the geometries journey through the export and meshing processes to be verified. If it is necesary to double check something in the X&Y axis without parallax distortion one of the sliders will move the Z plane. When this is done and is closed openEMS will set to work.

To execute the code, run:


octave hairpinfilter.sim.m

It will calculate for a minute. Then a window for CSXCAD will show up assuming the line calling it was not removed. This is useful for making sure the layout and mesh geometry exported correctly. Closing CSXCAD will launch openEMS solver. The result will look something like this:


$ octave hairpinfilter.sim.m 
z_bottom_copper =  1.5000
invoking AppCSXCAD, exit to continue script...

Available platform plugins are: minimal, offscreen, xcb.

args = "csxcad.xml" 
 ---------------------------------------------------------------------- 
 | openEMS 64bit -- version v0.0.35-45-gde23172
 | (C) 2010-2018 Thorsten Liebig <thorsten.liebig@gmx.de>  GPL license
 ---------------------------------------------------------------------- 
	Used external libraries:
		CSXCAD -- Version: v0.6.2-85-g55899d0
		hdf5   -- Version: 1.10.5
		          compiled against: HDF5 library version: 1.10.5
		tinyxml -- compiled against: 2.6.2
		fparser
		boost  -- compiled against: 1_65
		vtk -- Version: 8.1.0
		       compiled against: 8.1.0

Warning: No primitives found in property: AIR!

Create FDTD operator (compressed SSE + multi-threading)
FDTD simulation size: 104x104x30 -->
 324480 FDTD cells 
FDTD timestep is: 4.8853e-13 s; Nyquist rate: 487 timesteps @2.1016e+09 Hz
Excitation signal length is: 5865 timesteps (2.86523e-09s)
Max. number of timesteps: 1000000000 ( -->
 170503 * Excitation signal length)
Create FDTD engine (compressed SSE + multi-threading)
Warning: Unused primitive (type: Polygon) detected in property: COPPER_1!
Warning: Unused primitive (type: Polygon) detected in property: COPPER_1!
Warning: No primitives found in property: AIR!
Running FDTD engine... this may take a while... grab a cup of coffee?!?
[@        4s] Timestep:         1089 || Speed:   80.2 MC/s (4.048e-03 s/TS) || Energy: ~2.43e-18 (- 0.00dB)
[@        8s] Timestep:         2178 || Speed:   81.7 MC/s (3.973e-03 s/TS) || Energy: ~1.51e-15 (- 0.00dB)
[@       13s] Timestep:         3267 || Speed:   81.6 MC/s (3.976e-03 s/TS) || Energy: ~8.23e-15 (- 0.88dB)
[@       17s] Timestep:         4356 || Speed:   88.3 MC/s (3.676e-03 s/TS) || Energy: ~3.89e-15 (- 4.13dB)
[@       21s] Timestep:         5566 || Speed:   89.3 MC/s (3.635e-03 s/TS) || Energy: ~4.51e-15 (- 3.49dB)
[@       25s] Timestep:         6655 || Speed:   88.2 MC/s (3.679e-03 s/TS) || Energy: ~2.63e-15 (- 5.83dB)
[@       29s] Timestep:         7865 || Speed:   88.3 MC/s (3.673e-03 s/TS) || Energy: ~1.68e-15 (- 7.79dB)
[@       34s] Timestep:         9075 || Speed:   88.5 MC/s (3.665e-03 s/TS) || Energy: ~1.72e-15 (- 7.67dB)
[@       38s] Timestep:        10285 || Speed:   88.5 MC/s (3.666e-03 s/TS) || Energy: ~1.37e-15 (- 8.66dB)
[@       42s] Timestep:        11374 || Speed:   86.0 MC/s (3.772e-03 s/TS) || Energy: ~1.49e-15 (- 8.29dB)
[@       47s] Timestep:        12463 || Speed:   84.1 MC/s (3.858e-03 s/TS) || Energy: ~9.40e-16 (-10.30dB)
[@       51s] Timestep:        13552 || Speed:   85.7 MC/s (3.785e-03 s/TS) || Energy: ~1.33e-15 (- 8.78dB)
[@       55s] Timestep:        14641 || Speed:   84.6 MC/s (3.837e-03 s/TS) || Energy: ~9.05e-16 (-10.46dB)
[@       59s] Timestep:        15851 || Speed:   88.3 MC/s (3.673e-03 s/TS) || Energy: ~8.17e-16 (-10.91dB)
[@     1m04s] Timestep:        17061 || Speed:   91.4 MC/s (3.550e-03 s/TS) || Energy: ~1.06e-15 (- 9.78dB)
[@     1m08s] Timestep:        18271 || Speed:   90.9 MC/s (3.569e-03 s/TS) || Energy: ~8.46e-16 (-10.76dB)
[@     1m12s] Timestep:        19481 || Speed:   90.6 MC/s (3.581e-03 s/TS) || Energy: ~6.35e-16 (-12.00dB)
[@     1m17s] Timestep:        20691 || Speed:   91.5 MC/s (3.545e-03 s/TS) || Energy: ~7.23e-16 (-11.44dB)
[@     1m21s] Timestep:        21780 || Speed:   86.1 MC/s (3.767e-03 s/TS) || Energy: ~4.81e-16 (-13.21dB)
[@     1m25s] Timestep:        22869 || Speed:   83.6 MC/s (3.881e-03 s/TS) || Energy: ~5.52e-16 (-12.61dB)
[@     1m29s] Timestep:        23958 || Speed:   82.4 MC/s (3.939e-03 s/TS) || Energy: ~3.50e-16 (-14.59dB)
[@     1m33s] Timestep:        25047 || Speed:   87.7 MC/s (3.699e-03 s/TS) || Energy: ~3.64e-16 (-14.42dB)
[@     1m38s] Timestep:        26257 || Speed:   91.0 MC/s (3.567e-03 s/TS) || Energy: ~3.54e-16 (-14.54dB)
[@     1m42s] Timestep:        27467 || Speed:   89.4 MC/s (3.629e-03 s/TS) || Energy: ~1.53e-16 (-18.18dB)
[@     1m46s] Timestep:        28677 || Speed:   90.4 MC/s (3.588e-03 s/TS) || Energy: ~1.30e-16 (-18.88dB)
[@     1m51s] Timestep:        29887 || Speed:   92.3 MC/s (3.515e-03 s/TS) || Energy: ~1.86e-16 (-17.33dB)
[@     1m55s] Timestep:        31097 || Speed:   91.0 MC/s (3.564e-03 s/TS) || Energy: ~9.93e-17 (-20.06dB)
[@     1m59s] Timestep:        32307 || Speed:   90.7 MC/s (3.579e-03 s/TS) || Energy: ~5.95e-17 (-22.28dB)
[@     2m03s] Timestep:        33517 || Speed:   90.8 MC/s (3.575e-03 s/TS) || Energy: ~1.03e-16 (-19.89dB)
[@     2m08s] Timestep:        34727 || Speed:   90.6 MC/s (3.582e-03 s/TS) || Energy: ~6.70e-17 (-21.77dB)
[@     2m12s] Timestep:        35937 || Speed:   89.0 MC/s (3.646e-03 s/TS) || Energy: ~3.75e-17 (-24.29dB)
[@     2m16s] Timestep:        37026 || Speed:   85.9 MC/s (3.780e-03 s/TS) || Energy: ~8.71e-17 (-20.63dB)
[@     2m21s] Timestep:        38236 || Speed:   88.9 MC/s (3.648e-03 s/TS) || Energy: ~4.28e-17 (-23.72dB)
[@     2m25s] Timestep:        39325 || Speed:   87.9 MC/s (3.693e-03 s/TS) || Energy: ~6.66e-17 (-21.80dB)
[@     2m29s] Timestep:        40414 || Speed:   87.9 MC/s (3.691e-03 s/TS) || Energy: ~4.90e-17 (-23.13dB)
[@     2m33s] Timestep:        41503 || Speed:   87.7 MC/s (3.701e-03 s/TS) || Energy: ~5.30e-17 (-22.79dB)
[@     2m37s] Timestep:        42713 || Speed:   89.9 MC/s (3.608e-03 s/TS) || Energy: ~6.44e-17 (-21.94dB)
[@     2m42s] Timestep:        43923 || Speed:   90.0 MC/s (3.604e-03 s/TS) || Energy: ~3.47e-17 (-24.63dB)
[@     2m46s] Timestep:        45133 || Speed:   89.9 MC/s (3.610e-03 s/TS) || Energy: ~3.55e-17 (-24.53dB)
[@     2m50s] Timestep:        46343 || Speed:   90.0 MC/s (3.607e-03 s/TS) || Energy: ~4.79e-17 (-23.23dB)
[@     2m54s] Timestep:        47432 || Speed:   88.0 MC/s (3.688e-03 s/TS) || Energy: ~2.21e-17 (-26.58dB)
[@     2m58s] Timestep:        48521 || Speed:   87.9 MC/s (3.691e-03 s/TS) || Energy: ~3.79e-17 (-24.24dB)
[@     3m03s] Timestep:        49731 || Speed:   92.9 MC/s (3.493e-03 s/TS) || Energy: ~2.40e-17 (-26.23dB)
[@     3m07s] Timestep:        50941 || Speed:   91.0 MC/s (3.565e-03 s/TS) || Energy: ~1.37e-17 (-28.67dB)
[@     3m11s] Timestep:        52151 || Speed:   89.7 MC/s (3.617e-03 s/TS) || Energy: ~2.27e-17 (-26.47dB)
[@     3m16s] Timestep:        53361 || Speed:   90.1 MC/s (3.600e-03 s/TS) || Energy: ~1.78e-17 (-27.52dB)
[@     3m20s] Timestep:        54571 || Speed:   89.5 MC/s (3.625e-03 s/TS) || Energy: ~7.23e-18 (-31.44dB)
[@     3m24s] Timestep:        55781 || Speed:   89.1 MC/s (3.642e-03 s/TS) || Energy: ~1.21e-17 (-29.20dB)
[@     3m29s] Timestep:        56991 || Speed:   94.0 MC/s (3.453e-03 s/TS) || Energy: ~1.28e-17 (-28.95dB)
[@     3m33s] Timestep:        58201 || Speed:   92.6 MC/s (3.504e-03 s/TS) || Energy: ~5.04e-18 (-33.01dB)
[@     3m37s] Timestep:        59411 || Speed:   92.3 MC/s (3.515e-03 s/TS) || Energy: ~6.83e-18 (-31.69dB)
[@     3m41s] Timestep:        60621 || Speed:   92.4 MC/s (3.511e-03 s/TS) || Energy: ~9.66e-18 (-30.18dB)
[@     3m45s] Timestep:        61710 || Speed:   87.9 MC/s (3.691e-03 s/TS) || Energy: ~3.35e-18 (-34.78dB)
[@     3m49s] Timestep:        62799 || Speed:   88.3 MC/s (3.676e-03 s/TS) || Energy: ~8.29e-18 (-30.84dB)
[@     3m54s] Timestep:        64009 || Speed:   89.3 MC/s (3.634e-03 s/TS) || Energy: ~4.61e-18 (-33.39dB)
[@     3m58s] Timestep:        65219 || Speed:   88.7 MC/s (3.659e-03 s/TS) || Energy: ~3.23e-18 (-34.93dB)
[@     4m03s] Timestep:        66429 || Speed:   89.2 MC/s (3.638e-03 s/TS) || Energy: ~6.12e-18 (-32.17dB)
[@     4m07s] Timestep:        67639 || Speed:   92.5 MC/s (3.507e-03 s/TS) || Energy: ~4.51e-18 (-33.49dB)
[@     4m11s] Timestep:        68849 || Speed:   92.5 MC/s (3.508e-03 s/TS) || Energy: ~2.33e-18 (-36.36dB)
[@     4m15s] Timestep:        70059 || Speed:   91.7 MC/s (3.539e-03 s/TS) || Energy: ~4.10e-18 (-33.90dB)
[@     4m20s] Timestep:        71148 || Speed:   83.4 MC/s (3.890e-03 s/TS) || Energy: ~2.69e-18 (-35.74dB)
[@     4m24s] Timestep:        72237 || Speed:   83.4 MC/s (3.890e-03 s/TS) || Energy: ~3.08e-18 (-35.15dB)
[@     4m28s] Timestep:        73326 || Speed:   85.6 MC/s (3.792e-03 s/TS) || Energy: ~2.44e-18 (-36.16dB)
[@     4m32s] Timestep:        74415 || Speed:   85.9 MC/s (3.777e-03 s/TS) || Energy: ~2.22e-18 (-36.57dB)
[@     4m36s] Timestep:        75504 || Speed:   85.9 MC/s (3.775e-03 s/TS) || Energy: ~2.16e-18 (-36.68dB)
[@     4m40s] Timestep:        76593 || Speed:   85.5 MC/s (3.793e-03 s/TS) || Energy: ~1.55e-18 (-38.14dB)
[@     4m44s] Timestep:        77682 || Speed:   85.2 MC/s (3.809e-03 s/TS) || Energy: ~1.89e-18 (-37.27dB)
[@     4m49s] Timestep:        78771 || Speed:   84.0 MC/s (3.864e-03 s/TS) || Energy: ~1.05e-18 (-39.80dB)
[@     4m53s] Timestep:        79981 || Speed:   89.6 MC/s (3.620e-03 s/TS) || Energy: ~1.73e-18 (-37.64dB)
[@     4m57s] Timestep:        81070 || Speed:   87.8 MC/s (3.695e-03 s/TS) || Energy: ~6.57e-19 (-41.86dB)
[@     5m01s] Timestep:        82159 || Speed:   87.4 MC/s (3.714e-03 s/TS) || Energy: ~1.34e-18 (-38.76dB)
[@     5m05s] Timestep:        83248 || Speed:   87.2 MC/s (3.719e-03 s/TS) || Energy: ~5.78e-19 (-42.42dB)
[@     5m09s] Timestep:        84337 || Speed:   87.3 MC/s (3.715e-03 s/TS) || Energy: ~1.03e-18 (-39.92dB)
[@     5m13s] Timestep:        85426 || Speed:   87.6 MC/s (3.702e-03 s/TS) || Energy: ~5.35e-19 (-42.75dB)
[@     5m17s] Timestep:        86515 || Speed:   83.8 MC/s (3.871e-03 s/TS) || Energy: ~7.81e-19 (-41.10dB)
[@     5m22s] Timestep:        87604 || Speed:   84.8 MC/s (3.828e-03 s/TS) || Energy: ~5.12e-19 (-42.94dB)
[@     5m26s] Timestep:        88814 || Speed:   90.7 MC/s (3.578e-03 s/TS) || Energy: ~3.59e-19 (-44.47dB)
[@     5m30s] Timestep:        90024 || Speed:   91.1 MC/s (3.562e-03 s/TS) || Energy: ~6.70e-19 (-41.77dB)
[@     5m35s] Timestep:        91234 || Speed:   91.2 MC/s (3.559e-03 s/TS) || Energy: ~4.72e-19 (-43.29dB)
[@     5m39s] Timestep:        92444 || Speed:   91.8 MC/s (3.535e-03 s/TS) || Energy: ~2.38e-19 (-46.26dB)
[@     5m43s] Timestep:        93654 || Speed:   93.0 MC/s (3.487e-03 s/TS) || Energy: ~4.45e-19 (-43.55dB)
[@     5m47s] Timestep:        94864 || Speed:   92.5 MC/s (3.506e-03 s/TS) || Energy: ~4.07e-19 (-43.93dB)
[@     5m51s] Timestep:        95953 || Speed:   87.8 MC/s (3.698e-03 s/TS) || Energy: ~2.12e-19 (-46.76dB)
[@     5m56s] Timestep:        97163 || Speed:   88.4 MC/s (3.670e-03 s/TS) || Energy: ~3.65e-19 (-44.41dB)
[@     6m00s] Timestep:        98252 || Speed:   87.6 MC/s (3.706e-03 s/TS) || Energy: ~1.57e-19 (-48.06dB)
[@     6m04s] Timestep:        99341 || Speed:   83.5 MC/s (3.884e-03 s/TS) || Energy: ~2.84e-19 (-45.49dB)
[@     6m08s] Timestep:       100430 || Speed:   85.0 MC/s (3.818e-03 s/TS) || Energy: ~1.42e-19 (-48.52dB)
[@     6m12s] Timestep:       101519 || Speed:   84.8 MC/s (3.828e-03 s/TS) || Energy: ~2.14e-19 (-46.72dB)
[@     6m17s] Timestep:       102608 || Speed:   85.1 MC/s (3.812e-03 s/TS) || Energy: ~1.29e-19 (-48.93dB)
[@     6m21s] Timestep:       103697 || Speed:   85.5 MC/s (3.794e-03 s/TS) || Energy: ~1.58e-19 (-48.04dB)
[@     6m25s] Timestep:       104786 || Speed:   86.0 MC/s (3.771e-03 s/TS) || Energy: ~1.18e-19 (-49.31dB)
[@     6m29s] Timestep:       105875 || Speed:   85.9 MC/s (3.776e-03 s/TS) || Energy: ~1.14e-19 (-49.47dB)
[@     6m33s] Timestep:       106964 || Speed:   83.5 MC/s (3.885e-03 s/TS) || Energy: ~1.07e-19 (-49.73dB)
[@     6m37s] Timestep:       108053 || Speed:   82.9 MC/s (3.914e-03 s/TS) || Energy: ~8.14e-20 (-50.92dB)
Time for 108053 iterations with 324480.00 cells : 397.86 sec
Speed: 88.12 MCells/s 
                                                                               
                     |-----------------------------------------------------|   
                   0 |*************************************####************|   
                     |             $            $          #* # |***?***S11|   
                     |             $            $          #**##+###?###S21|   
                 -10 |-+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$#**$#$$$$$$$$$+-|   
                     |             $            $          #* $#           |   
                     |             $            $         ##* $##          |   
                 -20 |-+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$#$*$$$#$$$$$$$$+-|   
                     |             $            $         # * $ ##         |   
                     |             $            $        ##   $  #### #####|   
                 -30 |-+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$#$$$$$$$$$$####$+-|   
                     |             $            $       ##    $            |   
                     |             $            $       #     $            |   
                 -40 |-+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$##$$$$$$$$$$$$$$$$$+-|   
                     |             $            $    ##       $            |   
                     |             $            $#####        $            |   
                 -50 |-+$$$$$$$$$$$$$$$$$$$$$########$$$$$$$$$$$$$$$$$$$$+-|   
                     |             $       #### $             $            |   
                     |  ##  #####  +      ###   +             +            |   
                 -60 |-----------------------------------------------------|   
                     0            0.5           1            1.5           2   
                                        frequency (GHz)             

5. Results

The following graph shows S11 and S21:

More accurate results can be obtained by adding more empty space around the model and increasing the mesh density. For those familiar with the original example, differences in the output will be noticable. We believe this is entirely caused by differences in the mesh.

6. Afterword

As stated in the abstract this was not intended as a deep dive into the mechanics openEMS, just a working example.

For a wider perspective and background on openEMS the projects creator Thorsten Liebig gave a talk at FOSDEM 2019.

For more direct help with openEMS try the project's forum. For help with the pcb-rnd contact on of our developers via email or IRC as listed on our home page.

7. Acknowledgements

Thanks to Alyssa Baringer for her editing of this document.