Skip to contents

The results on this page were computed when the vignette was written (see vignettes/precompile.R); the code is what produced them.

A simulation answers “what does this circuit do”. Design asks “what should the values be” and “what happens when they are off by 5 %”. spice_sweep() runs one deck over many parameter sets, in parallel if asked, and returns everything as one tidy table.

Choosing the damping

A second-order RLC low-pass. The resistor sets the damping: too little and the step rings, too much and it crawls.

Schematic of the RLC low-pass: source Vin, series R1 and L1, capacitor C1 across the output.

rlc <- "rlc low-pass
vin in 0 pulse(0 1 0 1u) ac 1
r1 in mid 50
l1 mid out 1m
c1 out 0 1u
.tran 2u 1.2m
.end"

Critical damping is at

2 * sqrt(1e-3 / 1e-6)
#> [1] 63.24555
sw <- spice_sweep(rlc, r1 = c(10, 30, 63, 150))
tr <- sw |> spice_tidy(vars = "out")

ggplot(tr, aes(scale * 1e3, value, colour = factor(r1))) +
  geom_line(linewidth = 0.7) +
  labs(x = "time (ms)", y = "v(out) (V)", colour = "r1 (ohm)",
       title = "Step response against damping")
Plot of the simulation result described in the text.

plot of chunk damping

What tolerance does

Fix r1 at 30 ohm and give all three parts their tolerances: 5 % for the resistor and the inductor, 10 % for the capacitor. .mode = "zip" takes the vectors row by row instead of crossing them.

set.seed(42)
n <- 200
mc <- spice_sweep(rlc,
  r1 = rnorm(n, 30, 30 * 0.05 / 3),
  l1 = rnorm(n, 1e-3, 1e-3 * 0.05 / 3),
  c1 = rnorm(n, 1e-6, 1e-6 * 0.10 / 3),
  .mode = "zip", .threads = 4
)
out <- mc |> spice_tidy(vars = "out")
peak <- tapply(out$value, out$run, max)
summary((peak - 1) * 100)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>   15.92   17.78   18.46   18.49   19.16   20.97
ggplot(data.frame(overshoot = (peak - 1) * 100), aes(overshoot)) +
  geom_histogram(bins = 25, fill = "#b8741a", colour = "white") +
  labs(x = "overshoot (%)", y = "runs",
       title = "Overshoot over 200 circuits within tolerance")
Plot of the simulation result described in the text.

plot of chunk hist

Which part matters

The sweep keeps the parameters of every run, so the question is a regression away.

runs <- mc$params
runs$overshoot <- as.numeric((peak - 1) * 100)[match(runs$run, as.integer(names(peak)))]
fit <- lm(overshoot ~ scale(r1) + scale(l1) + scale(c1), data = runs)
round(coef(fit), 2)
#> (Intercept)   scale(r1)   scale(l1)   scale(c1) 
#>       18.49       -0.66        0.32       -0.70

One standard deviation of each part moves the overshoot by the amount shown, in percentage points: the resistor and the capacitor weigh about the same, the inductor half as much and the other way.