diff --git a/GridKit/CommonMath.md b/GridKit/CommonMath.md
index 65102e1d95..853ce34fb4 100644
--- a/GridKit/CommonMath.md
+++ b/GridKit/CommonMath.md
@@ -144,6 +144,7 @@ We implement an approximation to $\text{ReQU}$ using the logistic function.
| Inside | `inside` | Interior pulse indicator |
| Outside | `outside` | Outside-band indicator |
| Antiwindup | `antiwindup` | Anti-windup limited derivative |
+| Latch | `latch` | Set-reset memory rate |
### Maximum
@@ -364,3 +365,21 @@ The limits satisfy $\ell\le u$.
&\approx \phi(x,f)\,f
\end{aligned}
```
+
+### Latch
+
+The drives satisfy $a,b\in[0,1]$, and the scale satisfies $\mu\gt 4$.
+
+```math
+\begin{aligned}
+ \text{latch}(x,a,b)
+ &= (1-b)\left[a+(1-a)H(x-1/2)\right]-x \\[0pt]
+ &\approx (1-b)\left[a+(1-a)\sigma(x-1/2)\right]-x
+\end{aligned}
+```
+
+
+
+Here $x$ is the stored state, $a$ the set drive, and $b$ the reset drive.
+Full reset has priority. With both drives zero, as plotted, the stable states
+lie on either side of $1/2$ and approach zero and one as $\mu$ increases.
diff --git a/GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md b/GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md
new file mode 100644
index 0000000000..8cfdf64c0c
--- /dev/null
+++ b/GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md
@@ -0,0 +1,210 @@
+# BranchBreakers
+
+A two-terminal phasor-domain $\pi$ branch with independently controlled circuit
+breakers at both terminals.
+
+## Notes
+
+- Designed for [relay](../../Relay/README.md) protection: relay `trip` outputs
+ drive `trip1` and `trip2`, and relays measure the terminal-current outputs.
+- Terminal-current outputs publish values without Jacobian coupling to the bus
+ voltages or breaker states.
+
+## Block Diagram
+
+
+
+Figure 1: BranchBreakers model.
+
+## Model Parameters
+
+Symbol | Units | JSON | Description | Typical Value | Note
+-----------------|--------|---------|----------------------------------------|---------------|----------------------------
+$R$ | [p.u.] | `R` | Branch series resistance | |
+$X$ | [p.u.] | `X` | Branch series reactance | |
+$G$ | [p.u.] | `G` | Total line shunt conductance | 0.0 |
+$B$ | [p.u.] | `B` | Total line shunt susceptance | 0.0 |
+$G_\mathrm{mag}$ | [p.u.] | `Gmag` | Magnetizing shunt conductance at bus 1 | 0.0 |
+$B_\mathrm{mag}$ | [p.u.] | `Bmag` | Magnetizing shunt susceptance at bus 1 | 0.0 |
+$\tau$ | [p.u.] | `tap` | Off-nominal tap magnitude at bus 1 | 1.0 |
+$\theta$ | [rad] | `phase` | Phase-shift angle | 0.0 |
+$T_\mathrm{brk}$ | [s] | `Tbrk` | Breaker operating time | 0.05 | Time to $z_k = 1/2$ for a unit step command
+
+### Parameter Validation
+
+```math
+\begin{aligned}
+ R^2 + X^2 &> 0 \\
+ \tau &> 0 \\
+ T_\mathrm{brk} &\ge 0 \\
+ Y_{11}, Y_{22} &\ne 0
+\end{aligned}
+```
+
+All parameters must be finite real values.
+
+### Model Derived Parameters
+
+$Y_{mn}$ are entries of the closed-branch admittance matrix $\mathbf{Y}$.
+$Y_k^\mathrm{o}$ is the admittance seen at terminal $k$ with the opposite breaker open.
+
+```math
+\begin{aligned}
+ \epsilon_T &= 10^{-3}\ \mathrm{s} \\
+ T_\mathrm{brk} &\leftarrow \max(T_\mathrm{brk}, \epsilon_T) \\
+ T_\mathrm{latch} &= \dfrac{T_\mathrm{brk}}{\ln 2} \\
+ Y_\mathrm{br} &= \dfrac{1}{R + jX} \\
+ Y_\mathrm{mag} &= G_\mathrm{mag} + jB_\mathrm{mag} \\
+ Y_\mathrm{sh} &= G + jB \\
+ \mathbf{Y}_0 &=
+ \begin{bmatrix}
+ -Y_\mathrm{br} & Y_\mathrm{br} \\
+ Y_\mathrm{br} & -Y_\mathrm{br}
+ \end{bmatrix} \\
+ \mathbf{Y}_\mathrm{mag} &=
+ \begin{bmatrix}
+ -Y_\mathrm{mag} & 0 \\
+ 0 & 0
+ \end{bmatrix} \\
+ \mathbf{Y}_\mathrm{sh} &= \dfrac{1}{2}
+ \begin{bmatrix}
+ -Y_\mathrm{sh} & 0 \\
+ 0 & -Y_\mathrm{sh}
+ \end{bmatrix} \\
+ \mathbf{M} &=
+ \begin{bmatrix}
+ \tau^{-1} & 0 \\
+ 0 & \exp(j\theta)
+ \end{bmatrix} \\
+ \mathbf{Y} &= \mathbf{M}^{\dagger}\mathbf{Y}_0\mathbf{M}
+ + \mathbf{Y}_\mathrm{mag} + \mathbf{Y}_\mathrm{sh} \\
+ Y_1^\mathrm{o} &= Y_{11} - \dfrac{Y_{12}Y_{21}}{Y_{22}} \\
+ Y_2^\mathrm{o} &= Y_{22} - \dfrac{Y_{21}Y_{12}}{Y_{11}}
+\end{aligned}
+```
+
+## Model Ports
+
+Name | Port | Init | Description
+---------|--------|-------|--------------------------------------------
+`bus1` | Bus | Known | Required bus-1 terminal; the tapped side
+`bus2` | Bus | Known | Required bus-2 terminal
+`trip1` | Input | Known | Bus-1 breaker trip command
+`close1` | Input | Known | Bus-1 breaker close command
+`trip2` | Input | Known | Bus-2 breaker trip command
+`close2` | Input | Known | Bus-2 breaker close command
+`ir1` | Output | Known | Bus-1 terminal current, real component
+`ii1` | Output | Known | Bus-1 terminal current, imaginary component
+`ir2` | Output | Known | Bus-2 terminal current, real component
+`ii2` | Output | Known | Bus-2 terminal current, imaginary component
+
+Signal ports are optional. Attached inputs must be linked. Trip and close
+commands lie in $[0,1]$; unattached inputs default to zero.
+
+## Model Variables
+
+### Internal Variables
+
+#### Differential
+
+Symbol | Units | Description | Note
+-------|-------|----------------------------------|-----
+$z_1$ | [-] | Bus-1 breaker closed-state latch |
+$z_2$ | [-] | Bus-2 breaker closed-state latch |
+
+#### Algebraic
+
+None.
+
+### External Variables
+
+#### Differential
+
+None.
+
+#### Algebraic
+
+Symbol | Units | Description | Note
+---------|--------|----------------------------------------------|--------------------
+$V_{r1}$ | [p.u.] | Terminal voltage, real component, bus 1 | Owned by bus object
+$V_{i1}$ | [p.u.] | Terminal voltage, imaginary component, bus 1 | Owned by bus object
+$V_{r2}$ | [p.u.] | Terminal voltage, real component, bus 2 | Owned by bus object
+$V_{i2}$ | [p.u.] | Terminal voltage, imaginary component, bus 2 | Owned by bus object
+$s_1$ | [-] | Bus-1 breaker trip command | `trip1`
+$c_1$ | [-] | Bus-1 breaker close command | `close1`
+$s_2$ | [-] | Bus-2 breaker trip command | `trip2`
+$c_2$ | [-] | Bus-2 breaker close command | `close2`
+
+## Model Equations
+
+Smooth functions: [`above`](../../../../CommonMath.md#above),
+[`below`](../../../../CommonMath.md#below),
+[`latch`](../../../../CommonMath.md#latch).
+
+For readability, define:
+
+```math
+\begin{aligned}
+ u_1 &= \text{above}(z_1; 1/2) \\
+ u_2 &= \text{above}(z_2; 1/2) \\
+ \bar{u}_1 &= \text{below}(z_1; 1/2) \\
+ \bar{u}_2 &= \text{below}(z_2; 1/2)
+\end{aligned}
+```
+
+### Internal Equations
+
+#### Differential
+
+```math
+\begin{aligned}
+ 0 &= -\dot{z}_1 + \dfrac{1}{T_\mathrm{latch}}\text{latch}(z_1, c_1, s_1) \\
+ 0 &= -\dot{z}_2 + \dfrac{1}{T_\mathrm{latch}}\text{latch}(z_2, c_2, s_2)
+\end{aligned}
+```
+
+A close command drives the latch toward closed; a full trip command overrides it.
+
+#### Algebraic
+
+None.
+
+### External Equations
+
+```math
+\begin{aligned}
+ I_1 &= u_1\left[u_2(Y_{11} V_1 + Y_{12} V_2)
+ + \bar{u}_2 Y_1^\mathrm{o} V_1\right] \\
+ I_2 &= u_2\left[u_1(Y_{21} V_1 + Y_{22} V_2)
+ + \bar{u}_1 Y_2^\mathrm{o} V_2\right]
+\end{aligned}
+```
+
+The real and imaginary parts are added to the bus residuals, positive entering
+the buses.
+
+## Initialization
+
+```math
+\begin{aligned}
+ z_1 &\leftarrow 1 \\
+ z_2 &\leftarrow 1
+\end{aligned}
+```
+
+All internal derivatives initialize to zero.
+
+## Monitors
+
+Monitor | Units | Description | Note
+--------|--------|----------------------------------------------|--------------------------------
+`ir1` | [p.u.] | Terminal current, real component, bus 1 | $I_{r1}$
+`ii1` | [p.u.] | Terminal current, imaginary component, bus 1 | $I_{i1}$
+`im1` | [p.u.] | Terminal current magnitude, bus 1 | $\sqrt{I_{r1}^2 + I_{i1}^2}$
+`p1` | [p.u.] | Active power entering bus 1 | $V_{r1} I_{r1} + V_{i1} I_{i1}$
+`q1` | [p.u.] | Reactive power entering bus 1 | $V_{i1} I_{r1} - V_{r1} I_{i1}$
+`ir2` | [p.u.] | Terminal current, real component, bus 2 | $I_{r2}$
+`ii2` | [p.u.] | Terminal current, imaginary component, bus 2 | $I_{i2}$
+`im2` | [p.u.] | Terminal current magnitude, bus 2 | $\sqrt{I_{r2}^2 + I_{i2}^2}$
+`p2` | [p.u.] | Active power entering bus 2 | $V_{r2} I_{r2} + V_{i2} I_{i2}$
+`q2` | [p.u.] | Reactive power entering bus 2 | $V_{i2} I_{r2} - V_{r2} I_{i2}$
diff --git a/GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md b/GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md
new file mode 100644
index 0000000000..8e07be1264
--- /dev/null
+++ b/GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md
@@ -0,0 +1,140 @@
+# DefiniteTimeRelay
+
+A definite-time relay with optional lockout. Each relay models one
+[element](../README.md#elements).
+
+## Notes
+
+- Designed to measure a [BranchBreakers](../../Branch/BranchBreakers/README.md)
+ terminal current, a bus voltage, or a frequency, and to drive a BranchBreakers
+ `trip1` or `trip2` input.
+- The element operates above $M_\mathrm{pickup}$ if
+ $M_\mathrm{pickup} > M_\mathrm{ref}$ and below it otherwise.
+- With lockout, the relay commits to trip once $x$ passes $1/2$, so it may
+ measure the current it interrupts.
+
+## Model Parameters
+
+Symbol | Units | JSON | Description | Typical Value | Note
+--------------------|-----------|-----------|------------------|---------------|-----------------------------------------------------
+$M_\mathrm{pickup}$ | [p.u.] | `Mpickup` | Pickup level | | Required
+$M_\mathrm{ref}$ | [p.u.] | `Mref` | Reference level | | Required; 0 for current, 1 for voltage and frequency
+$T_\mathrm{trip}$ | [s] | `Ttrip` | Trip time | | Required; time to $x = 3/4$ for $p = 1$
+$s_\mathrm{lock}$ | [boolean] | `Lockout` | Lockout selector | `true` | `true` = lockout, `false` = self-reset
+
+### Parameter Validation
+
+```math
+\begin{aligned}
+ M_\mathrm{pickup} &> 0 \\
+ M_\mathrm{ref} &\ge 0 \\
+ M_\mathrm{pickup} &\ne M_\mathrm{ref} \\
+ T_\mathrm{trip} &\ge 0
+\end{aligned}
+```
+
+All real parameters must be finite.
+
+### Model Derived Parameters
+
+```math
+\begin{aligned}
+ \epsilon_T &= 10^{-3}\ \mathrm{s} \\
+ T_\mathrm{trip} &\leftarrow \max(T_\mathrm{trip}, \epsilon_T) \\
+ T_\mathrm{latch} &= \dfrac{T_\mathrm{trip}}{\ln 4}
+\end{aligned}
+```
+
+## Model Ports
+
+Name | Port | Init | Description
+-------|--------|-------|-------------------------------------------
+`mr` | Input | Known | Required measured quantity, real component
+`mi` | Input | Known | Measured quantity, imaginary component
+`trip` | Output | Known | Required trip command
+
+Unattached `mi` defaults to zero, so absolute frequency uses `mr` alone.
+
+## Model Variables
+
+### Internal Variables
+
+#### Differential
+
+Symbol | Units | Description | Note
+-------|-------|-------------|-----
+$x$ | [-] | Relay latch |
+
+#### Algebraic
+
+Symbol | Units | Description | Note
+-------|-------|--------------|-------
+$s$ | [-] | Trip command | `trip`
+
+### External Variables
+
+#### Differential
+
+None.
+
+#### Algebraic
+
+Symbol | Units | Description | Note
+-------|--------|----------------------------------------|-----
+$m_r$ | [p.u.] | Measured quantity, real component | `mr`
+$m_i$ | [p.u.] | Measured quantity, imaginary component | `mi`
+
+## Model Equations
+
+Smooth functions: [`above`](../../../../CommonMath.md#above),
+[`latch`](../../../../CommonMath.md#latch),
+[`logistic`](../../../../CommonMath.md#logistic-function) ($\sigma$).
+
+For readability, define:
+
+```math
+\begin{aligned}
+ p &= \text{logistic}\left(\dfrac{m_r^2 + m_i^2 - M_\mathrm{pickup}^2}{M_\mathrm{pickup}^2 - M_\mathrm{ref}^2}\right)
+\end{aligned}
+```
+
+### Internal Equations
+
+#### Differential
+
+```math
+\begin{aligned}
+ 0 &= -\dot{x} + \dfrac{1}{T_\mathrm{latch}}\text{latch}(x, p, (1 - s_\mathrm{lock})(1 - p))
+\end{aligned}
+```
+
+#### Algebraic
+
+```math
+\begin{aligned}
+ 0 &= -s + \text{above}(x; 3/4)
+\end{aligned}
+```
+
+### External Equations
+
+None.
+
+## Initialization
+
+```math
+\begin{aligned}
+ x &\leftarrow 0 \\
+ s &\leftarrow \text{above}(x; 3/4)
+\end{aligned}
+```
+
+All internal derivatives initialize to zero.
+
+## Monitors
+
+Monitor | Units | Description | Note
+--------|--------|-----------------------------|-----------------------
+`m` | [p.u.] | Measured quantity magnitude | $\sqrt{m_r^2 + m_i^2}$
+`x` | [-] | Relay latch | $x$
+`trip` | [-] | Trip command | $s$
diff --git a/GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md b/GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md
new file mode 100644
index 0000000000..cdb48128b1
--- /dev/null
+++ b/GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md
@@ -0,0 +1,157 @@
+# InverseTimeRelay
+
+An inverse-time overcurrent relay with optional lockout.
+
+## Notes
+
+- Designed to measure a [BranchBreakers](../../Branch/BranchBreakers/README.md)
+ terminal current and to drive a BranchBreakers `trip1` or `trip2` input.
+- Above pickup, the trip rises once $\int t_\mathrm{op}^{-1}\,dt = 1$, the IEEE
+ C37.112[^inverse-time] operate criterion.
+- With lockout, the relay commits to trip once $x$ passes $1/2$, so it may
+ measure the current it interrupts.
+
+## Model Parameters
+
+Symbol | Units | JSON | Description | Typical Value | Note
+--------------------|-----------|-----------|-------------------|---------------|---------------------------------------
+$I_\mathrm{pickup}$ | [p.u.] | `Ipickup` | Pickup current | | Required
+$D$ | [-] | `TD` | Time dial | | Required
+$A$ | [s] | `A` | Curve coefficient | | Required
+$B$ | [s] | `B` | Curve offset | | Required
+$\alpha$ | [-] | `alpha` | Curve exponent | | Required
+$t_r$ | [s] | `tr` | Reset constant | 0.0 | 0 for instantaneous reset
+$s_\mathrm{lock}$ | [boolean] | `Lockout` | Lockout selector | `true` | `true` = lockout, `false` = self-reset
+
+IEEE C37.112 curves:
+
+Curve | `A` | `B` | `alpha` | `tr`
+-------------------|--------|--------|---------|-----
+Moderately inverse | 0.0515 | 0.1140 | 0.02 | 4.85
+Very inverse | 19.61 | 0.491 | 2.0 | 21.6
+Extremely inverse | 28.2 | 0.1217 | 2.0 | 29.1
+
+### Parameter Validation
+
+```math
+\begin{aligned}
+ I_\mathrm{pickup} &> 0 \\
+ D &> 0 \\
+ A &> 0 \\
+ B &\ge 0 \\
+ \alpha &> 0 \\
+ t_r &\ge 0
+\end{aligned}
+```
+
+All real parameters must be finite.
+
+### Model Derived Parameters
+
+```math
+\begin{aligned}
+ \epsilon_T &= 10^{-3}\ \mathrm{s} \\
+ T_\mathrm{reset} &= \max(D t_r, \epsilon_T)
+\end{aligned}
+```
+
+## Model Ports
+
+Name | Port | Init | Description
+-------|--------|-------|-----------------------------------------------
+`ir` | Input | Known | Required measured current, real component
+`ii` | Input | Known | Required measured current, imaginary component
+`trip` | Output | Known | Required trip command
+
+## Model Variables
+
+### Internal Variables
+
+#### Differential
+
+Symbol | Units | Description | Note
+-------|-------|-------------|-----
+$x$ | [-] | Relay latch |
+
+#### Algebraic
+
+Symbol | Units | Description | Note
+-------|-------|--------------|-------
+$s$ | [-] | Trip command | `trip`
+
+### External Variables
+
+#### Differential
+
+None.
+
+#### Algebraic
+
+Symbol | Units | Description | Note
+-------|--------|---------------------------------------|-----
+$I_r$ | [p.u.] | Measured current, real component | `ir`
+$I_i$ | [p.u.] | Measured current, imaginary component | `ii`
+
+## Model Equations
+
+Smooth functions: [`above`](../../../../CommonMath.md#above),
+[`latch`](../../../../CommonMath.md#latch),
+[`logistic`](../../../../CommonMath.md#logistic-function) ($\sigma$),
+[`max`](../../../../CommonMath.md#maximum),
+[`min`](../../../../CommonMath.md#minimum).
+
+For readability, define:
+
+```math
+\begin{aligned}
+ M^2 &= \dfrac{I_r^2 + I_i^2}{I_\mathrm{pickup}^2} \\
+ p &= \text{logistic}(M^2 - 1) \\
+ K &= \text{max}(M^2, 1)^{\alpha/2} - 1 \\
+ \dfrac{1}{t_\mathrm{op}} &= \dfrac{K}{D(A + B K)} \\
+ \dfrac{1}{t_\mathrm{rs}} &= \dfrac{1 - \text{min}(M^2, 1)}{T_\mathrm{reset}} \\
+ \dfrac{1}{T_\mathrm{latch}} &= \ln 4\left(\dfrac{p}{t_\mathrm{op}} + \dfrac{1 - p}{t_\mathrm{rs}}\right)
+\end{aligned}
+```
+
+### Internal Equations
+
+#### Differential
+
+```math
+\begin{aligned}
+ 0 &= -\dot{x} + \dfrac{1}{T_\mathrm{latch}}\text{latch}(x, p, (1 - s_\mathrm{lock})(1 - p))
+\end{aligned}
+```
+
+#### Algebraic
+
+```math
+\begin{aligned}
+ 0 &= -s + \text{above}(x; 3/4)
+\end{aligned}
+```
+
+### External Equations
+
+None.
+
+## Initialization
+
+```math
+\begin{aligned}
+ x &\leftarrow 0 \\
+ s &\leftarrow \text{above}(x; 3/4)
+\end{aligned}
+```
+
+All internal derivatives initialize to zero.
+
+## Monitors
+
+Monitor | Units | Description | Note
+--------|--------|----------------------------|-----------------------
+`im` | [p.u.] | Measured current magnitude | $\sqrt{I_r^2 + I_i^2}$
+`x` | [-] | Relay latch | $x$
+`trip` | [-] | Trip command | $s$
+
+[^inverse-time]: [IEEE Std C37.112-2018, *IEEE Standard for Inverse-Time Characteristics Equations for Overcurrent Relays*](https://standards.ieee.org/ieee/C37.112/7036/).
diff --git a/GridKit/Model/PhasorDynamics/Relay/README.md b/GridKit/Model/PhasorDynamics/Relay/README.md
new file mode 100644
index 0000000000..a158b14ee3
--- /dev/null
+++ b/GridKit/Model/PhasorDynamics/Relay/README.md
@@ -0,0 +1,21 @@
+# Relay Models
+
+Relays measure network quantities and issue breaker commands.
+
+## Types
+
+- [DefiniteTimeRelay](DefiniteTimeRelay/README.md): Definite-time relay with optional lockout
+- [InverseTimeRelay](InverseTimeRelay/README.md): Inverse-time overcurrent relay with optional lockout
+
+## Elements
+
+ID[^device-numbers] | Short Name | Long Name | Model
+--------------------|------------|--------------------------|------------------
+27 | UV | Undervoltage | DefiniteTimeRelay
+50 | OC | Overcurrent | DefiniteTimeRelay
+51 | TOC | Inverse-time overcurrent | InverseTimeRelay
+59 | OV | Overvoltage | DefiniteTimeRelay
+81O | OF | Overfrequency | DefiniteTimeRelay
+81U | UF | Underfrequency | DefiniteTimeRelay
+
+[^device-numbers]: [IEEE Std C37.2-2022, *IEEE Standard for Electrical Power System Device Function Numbers, Acronyms, and Contact Designations*](https://standards.ieee.org/ieee/C37.2/6043/).
diff --git a/docs/Figures/CommonMath/latch.svg b/docs/Figures/CommonMath/latch.svg
new file mode 100644
index 0000000000..17415beb0b
--- /dev/null
+++ b/docs/Figures/CommonMath/latch.svg
@@ -0,0 +1,1193 @@
+
+
+
diff --git a/docs/Figures/CommonMath/latch.tex b/docs/Figures/CommonMath/latch.tex
new file mode 100644
index 0000000000..617aaa5cca
--- /dev/null
+++ b/docs/Figures/CommonMath/latch.tex
@@ -0,0 +1,27 @@
+\documentclass[tikz,border=4pt]{standalone}
+\usepackage{commonmath}
+
+\begin{document}
+\begin{tikzpicture}
+\begin{groupplot}[
+ cmbase,
+ x=3.2cm, y=2.2cm,
+ xmin=-0.12, xmax=1.22, ymin=-0.7, ymax=0.7,
+ xtick={0.5,1}, xticklabels={,},
+ ytick={-0.5,0.5}, yticklabels={$-1/2$,$1/2$},
+ domain=0:1,
+]
+\exactpanel
+ \addplot[domain=0:0.5] {-x};
+ \addplot[domain=0.5:1] {1-x};
+ \cmclosedpoints{(0.5,-0.5)}
+ \cmopenpoints{(0.5,0.5)}
+ \node[font=\scriptsize, anchor=south east, yshift=1.5pt] at (axis cs:0.5,0) {$1/2$};
+ \node[font=\scriptsize, anchor=south, yshift=1.5pt] at (axis cs:1,0) {$1$};
+
+\smoothpanel{sigmaS(x-0.5,\cmu) - x}
+ \node[font=\scriptsize, anchor=south east, yshift=1.5pt] at (axis cs:0.5,0) {$1/2$};
+ \node[font=\scriptsize, anchor=south, yshift=1.5pt] at (axis cs:1,0) {$1$};
+\end{groupplot}
+\end{tikzpicture}
+\end{document}
diff --git a/docs/Figures/PhasorDynamics/BranchBreakers/diagram.png b/docs/Figures/PhasorDynamics/BranchBreakers/diagram.png
new file mode 100644
index 0000000000..e1c5202a82
Binary files /dev/null and b/docs/Figures/PhasorDynamics/BranchBreakers/diagram.png differ
diff --git a/docs/Figures/PhasorDynamics/BranchBreakers/diagram.tex b/docs/Figures/PhasorDynamics/BranchBreakers/diagram.tex
new file mode 100644
index 0000000000..849b11ed42
--- /dev/null
+++ b/docs/Figures/PhasorDynamics/BranchBreakers/diagram.tex
@@ -0,0 +1,57 @@
+\documentclass[tikz,border=12pt]{standalone}
+\input{../../EMT/diagram-style.tex}
+
+\begin{document}
+\begin{tikzpicture}[emt diagram]
+
+% Branch network: shunts remain outside the bus-1 tap transformation.
+\node[hblock] (series) at (1.8, 0) {$Y_\mathrm{br}$};
+\node[vblock] (mag) at (-4.8, -1.6) {$Y_\mathrm{mag}$};
+\node[vblock] (shunt1) at (-3.0, -1.6) {$\dfrac{Y_\mathrm{sh}}{2}$};
+\node[vblock] (shunt2) at (4.8, -1.6) {$\dfrac{Y_\mathrm{sh}}{2}$};
+
+% Single-line transformer symbol, with the tap on the bus-1 side.
+\draw[semithick] (-1.35, 0) circle (0.45);
+\draw[semithick] (-0.75, 0) circle (0.45);
+\node[sig, anchor=south] at (-1.05, 0.6) {$\tau:1$};
+\node[sig, anchor=north] at (-1.05, -0.6) {$\theta$};
+
+% Single-line circuit breakers: switch contact with a cross at the fixed contact.
+\foreach \k/\x/\d in {1/-6.5/-1,2/6.5/1} {
+ \coordinate (pivot\k) at ({\x-\d*0.45}, 0);
+ \coordinate (contact\k) at ({\x+\d*0.45}, 0);
+ \draw[line] (pivot\k) -- ({\x+\d*0.32}, 0.55);
+ \draw[semithick] ($(contact\k)+(-0.12,-0.12)$) -- ($(contact\k)+(0.12,0.12)$);
+ \draw[semithick] ($(contact\k)+(-0.12,0.12)$) -- ($(contact\k)+(0.12,-0.12)$);
+ \draw[->, line] (\x-0.22, 2.1) -- (\x-0.22, 0.75);
+ \draw[->, line] (\x+0.22, 2.1) -- (\x+0.22, 0.75);
+ \node[sig, anchor=south] at (\x-0.22, 2.2) {$s_\k$};
+ \node[sig, anchor=south] at (\x+0.22, 2.2) {$c_\k$};
+}
+
+\draw[line] (pivot1) -- (-1.8, 0);
+\draw[line] (-0.3, 0) -- (series.west);
+\draw[line] (series.east) -- (pivot2);
+
+\foreach \shunt/\x in {mag/-4.8,shunt1/-3.0,shunt2/4.8} {
+ \node[signal] at (\x, 0) {};
+ \draw[line] (\x, 0) -- (\shunt.north);
+ \draw[line] (\shunt.south) -- (\x, -3.2);
+ \draw[semithick] (\x-0.35, -3.20) -- (\x+0.35, -3.20);
+ \draw[semithick] (\x-0.22, -3.35) -- (\x+0.22, -3.35);
+ \draw[semithick] (\x-0.09, -3.50) -- (\x+0.09, -3.50);
+}
+
+% Terminal voltages are bus-owned; current arrows point into the buses.
+\draw[->, line] (contact1) -- (-9.0, 0);
+\draw[->, line] (contact2) -- (9.0, 0);
+\node[signal, label={[sig]above:$V_1$}] at (-8.2, 0) {};
+\node[signal, label={[sig]above:$V_2$}] at (8.2, 0) {};
+\node[sig, anchor=east] at (-9.2, 0) {$I_1$};
+\node[sig, anchor=west] at (9.2, 0) {$I_2$};
+
+% Model enclosure.
+\draw[enclosure] (-7.5, -4.0) rectangle (7.5, 1.5);
+
+\end{tikzpicture}
+\end{document}
diff --git a/docs/GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md b/docs/GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md
new file mode 100644
index 0000000000..20453c276b
--- /dev/null
+++ b/docs/GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md
@@ -0,0 +1,6 @@
+# BranchBreakers
+
+```{include} ../../../../../../GridKit/Model/PhasorDynamics/Branch/BranchBreakers/README.md
+:start-line: 1
+:relative-images:
+```
diff --git a/docs/GridKit/Model/PhasorDynamics/Branch/README.md b/docs/GridKit/Model/PhasorDynamics/Branch/README.md
index f1adc4f9d0..2b3580ce62 100644
--- a/docs/GridKit/Model/PhasorDynamics/Branch/README.md
+++ b/docs/GridKit/Model/PhasorDynamics/Branch/README.md
@@ -1,5 +1,13 @@
# Branch
+```{toctree}
+:maxdepth: 4
+:titlesonly:
+:hidden:
+
+BranchBreakers
+```
+
```{include} ../../../../../GridKit/Model/PhasorDynamics/Branch/README.md
:start-line: 1
:relative-images:
diff --git a/docs/GridKit/Model/PhasorDynamics/README.md b/docs/GridKit/Model/PhasorDynamics/README.md
index cef6df173d..dc5f10aa48 100644
--- a/docs/GridKit/Model/PhasorDynamics/README.md
+++ b/docs/GridKit/Model/PhasorDynamics/README.md
@@ -13,6 +13,7 @@ Converter
Exciter
Governor
Load
+Relay
SignalNode
SignalSource
Stabilizer
diff --git a/docs/GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md b/docs/GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md
new file mode 100644
index 0000000000..b322101c15
--- /dev/null
+++ b/docs/GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md
@@ -0,0 +1,6 @@
+# DefiniteTimeRelay
+
+```{include} ../../../../../../GridKit/Model/PhasorDynamics/Relay/DefiniteTimeRelay/README.md
+:start-line: 1
+:relative-images:
+```
diff --git a/docs/GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md b/docs/GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md
new file mode 100644
index 0000000000..563aef879f
--- /dev/null
+++ b/docs/GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md
@@ -0,0 +1,6 @@
+# InverseTimeRelay
+
+```{include} ../../../../../../GridKit/Model/PhasorDynamics/Relay/InverseTimeRelay/README.md
+:start-line: 1
+:relative-images:
+```
diff --git a/docs/GridKit/Model/PhasorDynamics/Relay/README.md b/docs/GridKit/Model/PhasorDynamics/Relay/README.md
new file mode 100644
index 0000000000..f3fe92dd93
--- /dev/null
+++ b/docs/GridKit/Model/PhasorDynamics/Relay/README.md
@@ -0,0 +1,15 @@
+# Relay
+
+```{toctree}
+:maxdepth: 4
+:titlesonly:
+:hidden:
+
+DefiniteTimeRelay
+InverseTimeRelay
+```
+
+```{include} ../../../../../GridKit/Model/PhasorDynamics/Relay/README.md
+:start-line: 1
+:relative-images:
+```