Field & Potential Curves (E(r), V(r) vs. distance)
Specialized skill for the curve panel of electrostatics figures: plotting
electric field $E(r)$ and potential $V(r)$ as functions of distance $r$ (or
$\rho$) for a charge distribution. This is the right-hand panel that pairs
with a model diagram (see the general physics-model-figures skill for layout,
presets, and conventions — this skill narrows in on the E/V specifics).
The general skill covers any model+curves figure. Reach for this skill
when the curves are specifically $E$ and $V$ vs. distance — i.e. when you need
the piecewise functions, the dual-y-axis recipe, the ρ₀ reference, or the
canonical formula set below.
When to use
- Right panel shows $E(r)$ and/or $V(r)$ vs. distance for any charge geometry:
point charge, spherical shell, solid sphere, concentric shells, infinite line
charge, cylindrical shell, …
- You need the standard piecewise forms for any of these geometries.
- You need the dual-y-axis E/V layout (blue E on left, green V on right).
Do NOT use for: pure model diagrams with no curves (use physics-model-figures
single-panel variant), or non-electrostatics curves.
The canonical charge geometries
All forms use $k = 1/(4\pi\varepsilon_0)$. For line/cylindrical geometries the
grouping constant is $\dfrac{\lambda}{2\pi\varepsilon_0}$.
| Geometry |
$E(r)$ |
$V(r)$ |
| Point charge $q$ |
$kq/r^2$ |
$kq/r$ |
| Spherical shell (radius $R$, charge $Q$) |
$\begin{cases}0 & r<R \ kQ/r^2 & r\geq R\end{cases}$ |
$\begin{cases}kQ/R & r\leq R \ kQ/r & r>R\end{cases}$ |
| Solid sphere (radius $R$, charge $Q$) |
$\begin{cases}kQr/R^3 & r<R \ kQ/r^2 & r\geq R\end{cases}$ |
$\begin{cases}\dfrac{kQ}{2R}(3-r^2/R^2) & r\leq R \ kQ/r & r>R\end{cases}$ |
| Concentric shells ($Q_a$@$a$, $Q_b$@$b$) |
$\begin{cases}0 & r<a \ kQ_a/r^2 & a\leq r<b \ k(Q_a+Q_b)/r^2 & r\geq b\end{cases}$ |
$\begin{cases}kQ_a/a+kQ_b/b & r<a \ kQ_a/r+kQ_b/b & a\leq r<b \ k(Q_a+Q_b)/r & r\geq b\end{cases}$ |
| Infinite line charge $\lambda$ |
$\lambda/(2\pi\varepsilon_0\rho)$ |
$\dfrac{\lambda}{2\pi\varepsilon_0}\ln(\rho_0/\rho)$ |
| Cylindrical shell (radius $R$, $\lambda$) |
$\begin{cases}0 & \rho<R \ \lambda/(2\pi\varepsilon_0\rho) & \rho\geq R\end{cases}$ |
$\begin{cases}\dfrac{\lambda}{2\pi\varepsilon_0}\ln(\rho_0/R) & \rho<R \ \dfrac{\lambda}{2\pi\varepsilon_0}\ln(\rho_0/\rho) & \rho\geq R\end{cases}$ |
Continuity rules (sanity-check your plot)
- $E(r)$: discontinuous at a surface charge (shell, cylindrical shell)
— jumps by $\sigma/\varepsilon_0$. Continuous for a volume charge
(solid sphere) and at the inner edge of a shell (no surface there).
- $V(r)$: always continuous everywhere (potential can't jump).
- $E(R^+) \neq E(R^-)$ is expected for shells — don't "fix" it by forcing a
join. If your plotted E looks continuous at a shell surface, the function is
wrong.
The ρ₀ reference
$V$ for line/cylindrical geometries needs a reference distance $\rho_0$
where $V(\rho_0)=0$ (potential is only defined up to a constant). Pick
$\rho_0$ inside the plotted range and draw a vertical dashed line at it. A
common choice: $\rho_0 = 2R$ or $\rho_0$ at the right edge of the plot.
The dual-y-axis recipe
$E$ (N/C) and $V$ (V) have different units and scales → twin axes, not a
shared one. Single shared axis only when one curve dominates or units match
(e.g. src/point_charge_field.py puts both on one axis because values
coincide at small scales).
ax_e = fig.add_subplot(...)
ax_v = ax_e.twinx()
ax_e.set_ylabel("E (N/C)", color=BLU)
ax_e.tick_params(axis="y", labelcolor=BLU)
ax_e.spines["left"].set_color(BLU)
ax_v.set_ylabel("V (V)", color=GRN)
ax_v.tick_params(axis="y", labelcolor=GRN)
ax_v.spines["right"].set_color(GRN)
Colour convention (used across all repo figures): E = blue #1f4e9b,
V = green #2e8b57. Stick to it for visual consistency.
Choosing y-axis ranges
$E$ and $V$ ranges differ per geometry — set them per figure, not via a preset:
- Shells/solid sphere: $E$ peaks at the surface then falls; set $E$ range
to comfortably clear $E(R)$. $V$ is positive, peaks at centre; range clears
$V(0)$. e.g. solid sphere $V(0)=3kQ/(2R)$ needs a taller V axis than a shell.
- Line/cylindrical: $E\to\infty$ near $\rho\to0$ — start the x-axis at a
small positive value (e.g. $0.1\rho_0$), not 0. $V$ goes negative for
$\rho>\rho_0$ → the V y-axis must include a negative range (e.g.
set_ylim(-30, 20)).
Plotting piecewise functions
Build separate arrays per region and plot each segment. Don't use a single
array with np.where — it hides the discontinuities and can draw spurious
vertical lines at boundaries.
r1 = np.linspace(0, R, 200) # inside
r2 = np.linspace(R, 4 * R, 400) # outside
ax_e.plot(r1, np.zeros_like(r1), color=BLU, lw=2.8) # E=0 inside
ax_e.plot(r2, K * Q / r2**2, color=BLU, lw=2.8) # E=kQ/r² outside
Required annotations
Every E/V curve panel in this repo includes these — see existing scripts for
exact code:
- Vertical dashed line(s) at each region boundary ($r=R$, $r=a$, $r=b$,
$\rho=\rho_0$) in grey (
#888888), with a small label.
- Marked points at key values: white-filled circle (
facecolor="white",
edgecolor=<curve color>) + annotate with the numeric value. For the
central maximum $V(0)$ use a fraction form
($V_0 = \frac{3kQ}{2R}$) alongside the number.
- Asymptotes:
axhline(0, ls="--") with a label. Line-charge figures add
$V\to -\infty$. Mark these near the right edge with
ha="right", style="italic".
- Formula boxes (upper area of each axis): the full piecewise
expression, one line per region, stacked with
\n. See gotcha below for
the mathtext limitation. No numeric constants (no k=9e9, Q=...) — per
repo convention.
- Legend (
loc="upper right" or "center right"): one entry per curve,
label = $E(r)$ / $V(r)$ (function name only, not the formula).
- Light grid:
ax_e.grid(True, color="#dddddd", lw=0.6).
- Subtitle via
ax.set_title(...), no suptitle.
Marked-point label placement (avoid overlaps)
When $E$ and $V$ curves cross or sit close (common near $r=R$), their marked
labels collide. Offset them on opposite sides of the point:
ax_e.annotate(f"E = {e:g}", xy=(r, e), xytext=(8, +14), textcoords="offset points", ...)
ax_v.annotate(f"V = {v:g}", xy=(r, v), xytext=(8, -14), textcoords="offset points", ...)
Gotchas (hard-won from 6 figures in this repo)
matplotlib mathtext has NO \begin{cases} / \dfrac array layout.
Those are LaTeX-only. Stack piecewise lines manually with \n and
linespacing. When a line contains a tall fraction (\dfrac), add an extra
blank line ("\n\n") and bump linespacing to ~2.0, or the fraction
collides with the line below. See src/charged_cylindrical_shell.py for the
workaround.
- mathtext unsupported LaTeX commands (raise
ParseSyntaxException):
\! (negative thin space), \tfrac, \textstyle, \substack. Use \frac
(not \tfrac), drop \!, and \dfrac is OK in mathtext. If a formula
throws a parse error, suspect these first.
- Piecewise at a surface → E jumps. If your shell/cylinder E curve looks
smooth across $r=R$, you've silently joined the segments — that's
physically wrong. Plot inside and outside as separate arrays.
- Line-charge $V$ goes negative for $\rho>\rho_0$. Set the V y-axis to
include negatives, and draw a $V=0$ reference line. Don't clip it to
$\geq 0$.
twinx() colour binding: the left spine stays the left axis's colour,
the right spine the right axis's. Set both explicitly or the axes look
uncoloured.
- 3D model panel beside an E/V curve panel: if the left model panel needs
3D (e.g. line charge, where E is perpendicular to the wire in 3D), use
projection="3d" and the Arrow3D subclass (see src/infinite_line_charge.py,
src/magnetic_field_loop.py). The right E/V panel stays 2D.
Model-panel hints specific to E/V geometries
These are the model-panel conventions that recur across E/V figures (the
general skill covers the rest):
- 2D cross-section is correct for spherical shell, solid sphere,
concentric shells, cylindrical shell — E lies in the plane of the section.
- 3D is needed when E is perpendicular to a line (infinite line charge):
the wire runs along $z$, E arrows radiate horizontally. A 2D side-view would
misrepresent the field as parallel to the wire.
- Arrow length encodes field magnitude: radial arrows get shorter with
distance ($\propto 1/r^n$). Inside a conductor/shell interior, draw no
arrows ($E=0$) — optionally mark a few
× or an "E=0" label.
- Reference circle: dashed circle at the reference radius, labelled
ρ or
R, to anchor the distance variable visually.
- Inset cylinder (cylindrical shell only): a small 3D cylinder in the
corner with an arrow to the main circle clarifies "this is a cross-section".
See
src/charged_cylindrical_shell.py lines ~98–126; the arrow direction is
controlled by an ANGLE constant there.
Reference implementations
Closest match per geometry — copy as a starting point:
| Target geometry |
Copy from |
| Point charge |
src/point_charge_field.py (single shared axis, no twin) |
| Spherical shell |
src/charged_sphere.py |
| Solid sphere |
src/charged_solid_sphere.py (note V(0)=3kQ/2R peak) |
| Concentric shells |
src/concentric_shells.py (3-piece piecewise) |
| Infinite line charge |
src/infinite_line_charge.py (3D model, V<0 region) |
| Cylindrical shell |
src/charged_cylindrical_shell.py (2D + cylinder inset) |
To adapt one: swap the constants block, the piecewise arrays in
draw_curves, and the region boundaries (vertical lines + labels). The
dual-axis scaffolding, annotation style, and formula-box layout stay the same.
1---2name: field-potential-curves3description: Generate E(r) and V(r) curve panels (electric field + potential vs. distance) for charge distributions — point charge, spherical shell, solid sphere, concentric shells, line charge, cylindrical shell. Specialized companion to physics-model-figures for figures whose right panel is a piecewise E/V vs. distance plot. Use when the task is specifically about electric field and potential curves.4---56# Field & Potential Curves (E(r), V(r) vs. distance)78Specialized skill for the **curve panel** of electrostatics figures: plotting9electric field $E(r)$ and potential $V(r)$ as functions of distance $r$ (or10$\rho$) for a charge distribution. This is the right-hand panel that pairs11with a model diagram (see the general `physics-model-figures` skill for layout,12presets, and conventions — this skill narrows in on the E/V specifics).1314The general skill covers *any* model+curves figure. Reach for **this** skill15when the curves are specifically $E$ and $V$ vs. distance — i.e. when you need16the piecewise functions, the dual-y-axis recipe, the ρ₀ reference, or the17canonical formula set below.1819## When to use2021- Right panel shows $E(r)$ and/or $V(r)$ vs. distance for any charge geometry:22 point charge, spherical shell, solid sphere, concentric shells, infinite line23 charge, cylindrical shell, …24- You need the **standard piecewise forms** for any of these geometries.25- You need the **dual-y-axis** E/V layout (blue E on left, green V on right).2627Do NOT use for: pure model diagrams with no curves (use `physics-model-figures`28single-panel variant), or non-electrostatics curves.2930## The canonical charge geometries3132All forms use $k = 1/(4\pi\varepsilon_0)$. For line/cylindrical geometries the33grouping constant is $\dfrac{\lambda}{2\pi\varepsilon_0}$.3435| Geometry | $E(r)$ | $V(r)$ |36|---|---|---|37| **Point charge $q$** | $kq/r^2$ | $kq/r$ |38| **Spherical shell (radius $R$, charge $Q$)** | $\begin{cases}0 & r<R \\ kQ/r^2 & r\geq R\end{cases}$ | $\begin{cases}kQ/R & r\leq R \\ kQ/r & r>R\end{cases}$ |39| **Solid sphere (radius $R$, charge $Q$)** | $\begin{cases}kQr/R^3 & r<R \\ kQ/r^2 & r\geq R\end{cases}$ | $\begin{cases}\dfrac{kQ}{2R}(3-r^2/R^2) & r\leq R \\ kQ/r & r>R\end{cases}$ |40| **Concentric shells ($Q_a$@$a$, $Q_b$@$b$)** | $\begin{cases}0 & r<a \\ kQ_a/r^2 & a\leq r<b \\ k(Q_a+Q_b)/r^2 & r\geq b\end{cases}$ | $\begin{cases}kQ_a/a+kQ_b/b & r<a \\ kQ_a/r+kQ_b/b & a\leq r<b \\ k(Q_a+Q_b)/r & r\geq b\end{cases}$ |41| **Infinite line charge $\lambda$** | $\lambda/(2\pi\varepsilon_0\rho)$ | $\dfrac{\lambda}{2\pi\varepsilon_0}\ln(\rho_0/\rho)$ |42| **Cylindrical shell (radius $R$, $\lambda$)** | $\begin{cases}0 & \rho<R \\ \lambda/(2\pi\varepsilon_0\rho) & \rho\geq R\end{cases}$ | $\begin{cases}\dfrac{\lambda}{2\pi\varepsilon_0}\ln(\rho_0/R) & \rho<R \\ \dfrac{\lambda}{2\pi\varepsilon_0}\ln(\rho_0/\rho) & \rho\geq R\end{cases}$ |4344### Continuity rules (sanity-check your plot)4546- $E(r)$: **discontinuous** at a **surface** charge (shell, cylindrical shell)47 — jumps by $\sigma/\varepsilon_0$. **Continuous** for a **volume** charge48 (solid sphere) and at the inner edge of a shell (no surface there).49- $V(r)$: **always continuous** everywhere (potential can't jump).50- $E(R^+) \neq E(R^-)$ is *expected* for shells — don't "fix" it by forcing a51 join. If your plotted E looks continuous at a shell surface, the function is52 wrong.5354### The ρ₀ reference5556$V$ for line/cylindrical geometries needs a **reference distance** $\rho_0$57where $V(\rho_0)=0$ (potential is only defined up to a constant). Pick58$\rho_0$ inside the plotted range and draw a vertical dashed line at it. A59common choice: $\rho_0 = 2R$ or $\rho_0$ at the right edge of the plot.6061## The dual-y-axis recipe6263$E$ (N/C) and $V$ (V) have different units and scales → **twin axes**, not a64shared one. Single shared axis only when one curve dominates or units match65(e.g. `src/point_charge_field.py` puts both on one axis because values66coincide at small scales).6768```python69ax_e = fig.add_subplot(...)70ax_v = ax_e.twinx()7172ax_e.set_ylabel("E (N/C)", color=BLU)73ax_e.tick_params(axis="y", labelcolor=BLU)74ax_e.spines["left"].set_color(BLU)7576ax_v.set_ylabel("V (V)", color=GRN)77ax_v.tick_params(axis="y", labelcolor=GRN)78ax_v.spines["right"].set_color(GRN)79```8081Colour convention (used across all repo figures): **E = blue `#1f4e9b`**,82**V = green `#2e8b57`**. Stick to it for visual consistency.8384### Choosing y-axis ranges8586$E$ and $V$ ranges differ per geometry — set them per figure, not via a preset:8788- **Shells/solid sphere**: $E$ peaks at the surface then falls; set $E$ range89 to comfortably clear $E(R)$. $V$ is positive, peaks at centre; range clears90 $V(0)$. e.g. solid sphere $V(0)=3kQ/(2R)$ needs a taller V axis than a shell.91- **Line/cylindrical**: $E\to\infty$ near $\rho\to0$ — start the x-axis at a92 small positive value (e.g. $0.1\rho_0$), not 0. $V$ goes **negative** for93 $\rho>\rho_0$ → the V y-axis must include a negative range (e.g.94 `set_ylim(-30, 20)`).9596### Plotting piecewise functions9798Build separate arrays per region and `plot` each segment. Don't use a single99array with `np.where` — it hides the discontinuities and can draw spurious100vertical lines at boundaries.101102```python103r1 = np.linspace(0, R, 200) # inside104r2 = np.linspace(R, 4 * R, 400) # outside105ax_e.plot(r1, np.zeros_like(r1), color=BLU, lw=2.8) # E=0 inside106ax_e.plot(r2, K * Q / r2**2, color=BLU, lw=2.8) # E=kQ/r² outside107```108109## Required annotations110111Every E/V curve panel in this repo includes these — see existing scripts for112exact code:1131141. **Vertical dashed line(s)** at each region boundary ($r=R$, $r=a$, $r=b$,115 $\rho=\rho_0$) in grey (`#888888`), with a small label.1162. **Marked points** at key values: white-filled circle (`facecolor="white"`,117 `edgecolor=<curve color>`) + `annotate` with the numeric value. For the118 central maximum $V(0)$ use a fraction form119 (`$V_0 = \frac{3kQ}{2R}$`) alongside the number.1203. **Asymptotes**: `axhline(0, ls="--")` with a label. Line-charge figures add121 $V\to -\infty$. Mark these near the right edge with122 `ha="right", style="italic"`.1234. **Formula boxes** (upper area of each axis): the **full piecewise124 expression**, one line per region, stacked with `\n`. See gotcha below for125 the mathtext limitation. **No numeric constants** (no `k=9e9, Q=...`) — per126 repo convention.1275. **Legend** (`loc="upper right"` or `"center right"`): one entry per curve,128 label = `$E(r)$` / `$V(r)$` (function name only, not the formula).1296. **Light grid**: `ax_e.grid(True, color="#dddddd", lw=0.6)`.1307. **Subtitle** via `ax.set_title(...)`, **no** `suptitle`.131132### Marked-point label placement (avoid overlaps)133134When $E$ and $V$ curves cross or sit close (common near $r=R$), their marked135labels collide. Offset them on **opposite sides** of the point:136```python137ax_e.annotate(f"E = {e:g}", xy=(r, e), xytext=(8, +14), textcoords="offset points", ...)138ax_v.annotate(f"V = {v:g}", xy=(r, v), xytext=(8, -14), textcoords="offset points", ...)139```140141## Gotchas (hard-won from 6 figures in this repo)142143- **`matplotlib` mathtext has NO `\begin{cases}` / `\dfrac` array layout.**144 Those are LaTeX-only. Stack piecewise lines manually with `\n` and145 `linespacing`. When a line contains a tall fraction (`\dfrac`), add an extra146 blank line (`"\n\n"`) and bump `linespacing` to ~2.0, or the fraction147 collides with the line below. See `src/charged_cylindrical_shell.py` for the148 workaround.149- **mathtext unsupported LaTeX commands** (raise `ParseSyntaxException`):150 `\!` (negative thin space), `\tfrac`, `\textstyle`, `\substack`. Use `\frac`151 (not `\tfrac`), drop `\!`, and `\dfrac` *is* OK in mathtext. If a formula152 throws a parse error, suspect these first.153- **Piecewise at a surface → E jumps.** If your shell/cylinder E curve looks154 smooth across $r=R$, you've silently joined the segments — that's155 physically wrong. Plot inside and outside as separate arrays.156- **Line-charge $V$ goes negative** for $\rho>\rho_0$. Set the V y-axis to157 include negatives, and draw a $V=0$ reference line. Don't clip it to158 $\geq 0$.159- **`twinx()` colour binding**: the left spine stays the left axis's colour,160 the right spine the right axis's. Set both explicitly or the axes look161 uncoloured.162- **3D model panel beside an E/V curve panel**: if the left model panel needs163 3D (e.g. line charge, where E is perpendicular to the wire in 3D), use164 `projection="3d"` and the `Arrow3D` subclass (see `src/infinite_line_charge.py`,165 `src/magnetic_field_loop.py`). The right E/V panel stays 2D.166167## Model-panel hints specific to E/V geometries168169These are the model-panel conventions that recur across E/V figures (the170general skill covers the rest):171172- **2D cross-section** is correct for spherical shell, solid sphere,173 concentric shells, cylindrical shell — E lies in the plane of the section.174- **3D** is needed when E is perpendicular to a line (infinite line charge):175 the wire runs along $z$, E arrows radiate horizontally. A 2D side-view would176 misrepresent the field as parallel to the wire.177- **Arrow length encodes field magnitude**: radial arrows get shorter with178 distance ($\propto 1/r^n$). Inside a conductor/shell interior, draw **no**179 arrows ($E=0$) — optionally mark a few `×` or an "E=0" label.180- **Reference circle**: dashed circle at the reference radius, labelled `ρ` or181 `R`, to anchor the distance variable visually.182- **Inset cylinder** (cylindrical shell only): a small 3D cylinder in the183 corner with an arrow to the main circle clarifies "this is a cross-section".184 See `src/charged_cylindrical_shell.py` lines ~98–126; the arrow direction is185 controlled by an `ANGLE` constant there.186187## Reference implementations188189Closest match per geometry — copy as a starting point:190191| Target geometry | Copy from |192|---|---|193| Point charge | `src/point_charge_field.py` (single shared axis, no twin) |194| Spherical shell | `src/charged_sphere.py` |195| Solid sphere | `src/charged_solid_sphere.py` (note V(0)=3kQ/2R peak) |196| Concentric shells | `src/concentric_shells.py` (3-piece piecewise) |197| Infinite line charge | `src/infinite_line_charge.py` (3D model, V<0 region) |198| Cylindrical shell | `src/charged_cylindrical_shell.py` (2D + cylinder inset) |199200To adapt one: swap the **constants block**, the **piecewise arrays in201`draw_curves`**, and the **region boundaries** (vertical lines + labels). The202dual-axis scaffolding, annotation style, and formula-box layout stay the same.