Skip to content
Get started

3. Build and run a divider

Imagine measuring the midpoint between a 1 kΩ upper resistor and a 2 kΩ lower resistor connected across an ideal 12 V source. The measuring instrument draws negligible current. Will its reading be closer to 0 V or 12 V?

The larger lower resistance needs the larger voltage drop to carry the same current. We therefore expect the midpoint to sit above half the supply. We will derive the precise answer, run the model, and then shorten its source without changing its laws.

Let II flow from the positive supply through both resistors toward ground. Charge conservation gives equal series current, while Ohm’s law gives each drop. Summing the drops yields

Vs=R1I+R2I,I=VsR1+R2,Vout=R2R1+R2Vs.V_s=R_1I+R_2I, \qquad I=\frac{V_s}{R_1+R_2}, \qquad V_{\mathrm{out}}=\frac{R_2}{R_1+R_2}V_s.

This is the series-resistance argument in OpenStax §10.2, applied to the midpoint measurement. For the specified values, the independent predictions are:

Quantity Calculation Prediction
Series current 12/(1000+2000)12/(1000+2000) 0.004 A
Midpoint potential 2000(0.004)2000(0.004) 8 V
Upper absorbed power 1000(0.004)21000(0.004)^2 0.016 W
Lower absorbed power 2000(0.004)22000(0.004)^2 0.032 W
Source absorbed power 12(0.004)-12(0.004) −0.048 W

The power sum is zero. Write down these values before running anything. They will let you distinguish a mistaken sign or resistance from roundoff.

Use the environment from Get started. In the folder containing .venv, save this complete listing as divider.eqi:

divider.eqi — complete component definitions and circuit
public connector Pin {
across voltage: kg * m ^ 2 / (s ^ 3 * A);
through current: A;
}
public component IdealVoltageSource(
parameter voltage: kg * m ^ 2 / (s ^ 3 * A),
port positive: Pin,
port negative: Pin
) {
relation law {
positive.voltage - negative.voltage - voltage = 0;
positive.current + negative.current = 0;
}
}
public component Resistor(
parameter resistance: kg * m ^ 2 / (s ^ 3 * A ^ 2),
port positive: Pin,
port negative: Pin
) {
relation law {
positive.voltage - negative.voltage - resistance * positive.current = 0;
positive.current + negative.current = 0;
}
}
public component Ground(
port terminal: Pin
) {
relation law {
terminal.voltage = 0;
}
}
/// Ideal 12 V divider with an explicit zero-voltage reference.
/// Ohm and Kirchhoff give 4 mA through both resistors and an 8 V midpoint.
model VoltageDivider() {
instance source: IdealVoltageSource(voltage = 12[V]);
instance upper: Resistor(resistance = 1[kOhm]);
instance lower: Resistor(resistance = 2[kOhm]);
instance ground: Ground();
connect source.positive, upper.positive;
connect upper.negative, lower.positive;
connect lower.negative, source.negative, ground.terminal;
observable current: A = upper.positive.current;
observable midpoint: V = lower.positive.voltage;
observable upper_power: W = (upper.positive.voltage - upper.negative.voltage) * upper.positive.current;
observable lower_power: W = (lower.positive.voltage - lower.negative.voltage) * lower.positive.current;
observable source_power: W = (source.positive.voltage - source.negative.voltage) * source.positive.current;
}

Read the listing from its laws to its connections:

  • Pin carries a potential and a signed current.
  • IdealVoltageSource prescribes a difference and conserves terminal current.
  • Resistor writes vRi=0v-Ri=0 and conserves terminal current.
  • Ground sets the reference potential.
  • VoltageDivider creates instances and connects their terminals.

The model uses 1[kOhm], while the component’s resistance declaration uses base dimensions. Since Ω=kgm2/(s3A2)\Omega=\mathrm{kg\,m^2/(s^3\,A^2)}, these describe the same physical quantity. The compact unit is a convenience for the reader.

The observable declarations ask for expressions to read after solving. They do not add a second voltage or current to the circuit.

Save the following as run-divider.py beside divider.eqi:

run-divider.py
import eqiora
def resolve(model):
return eqiora.resolve(model, solve=eqiora.solve.Linear(
algorithm=eqiora.solve.LinearSolver.SparseLu,
preconditioner=eqiora.solve.Preconditioner.Identity,
reduction=eqiora.solve.Reduction.Fast,
provider=eqiora.solve.SolverProvider.faer(),
relative_tolerance=1e-12, absolute_tolerance=1e-14,
maximum_iterations=100,
))
model = eqiora.compile(path="divider.eqi", entry="VoltageDivider")
plan = resolve(model)
result = eqiora.run(plan, state=eqiora.State.initial(plan))
for name, unit in (("current", "A"), ("midpoint", "V"), ("upper_power", "W"), ("lower_power", "W"), ("source_power", "W")):
print(f"{name}: {result.observe(model.observable(name)).value} {unit}")
Terminal windowbash
uv run --no-project --python .venv/bin/python python run-divider.py

The Python program compiles the equations, chooses a linear solver, creates the initial state for that plan, executes it, and observes the result. This steady algebraic problem needs no time integrator. The sparse LU choice belongs to the calculation, while the resistances belong to the model.

Expect five lines close to the values in the table. For this circuit and the listed solver settings, compare midpoint voltage within 101010^{-10} V and the current and individual powers within 101210^{-12} A or W, respectively. The sum of three power errors can accumulate, so compare the summed power with zero within 3×10123\times10^{-12} W. These are absolute comparison bounds for these particular scales, not the number of physically meaningful digits in a real resistor measurement.

The same laws are already in the standard package

Section titled “The same laws are already in the standard package”

You have now written and run every component law. The definitions at the top are also the definitions supplied by Eqiora.Electrical.Basic. The shorter application imports them:

examples/voltage-divider/src/main.eqi
import Eqiora.Electrical.Basic.basic as electrical;
/// Ideal 12 V divider with an explicit zero-voltage reference.
/// Ohm and Kirchhoff give 4 mA through both resistors and an 8 V midpoint.
model VoltageDivider() {
instance source: electrical.IdealVoltageSource(voltage = 12[V]);
instance upper: electrical.Resistor(resistance = 1[kOhm]);
instance lower: electrical.Resistor(resistance = 2[kOhm]);
instance ground: electrical.Ground();
connect source.positive, upper.positive;
connect upper.negative, lower.positive;
connect lower.negative, source.negative, ground.terminal;
observable current: A = upper.positive.current;
observable midpoint: V = lower.positive.voltage;
observable upper_power: W = (upper.positive.voltage - upper.negative.voltage) * upper.positive.current;
observable lower_power: W = (lower.positive.voltage - lower.negative.voltage) * lower.positive.current;
observable source_power: W = (source.positive.voltage - source.negative.voltage) * source.positive.current;
}

The electrical. prefix names the imported definitions. The three connections, four component instances, and five observations retain their meaning. The standard library has saved you from maintaining those common definitions.

To run this packaged version, use the checkout kept during Get started:

Terminal windowbash
uv run --no-project --python .venv/bin/python python eqiora-source/examples/voltage-divider/run.py

The script brings the bundled dependency into a local store and compiles the project’s VoltageDivider. Its numerical solver and observations are the same as above. Open the complete packaged runner and the actual imported component definitions.

In your direct divider.eqi, change the source parameter from 12[V] to 24[V]. Predict 8 mA, 16 V, 0.064 W, 0.128 W, and −0.192 W, then rerun the same direct script. Current and voltage double; powers quadruple.

Next restore 12 V and change both resistances by the same factor of ten. The output voltage should remain 8 V, but current and power should each become one tenth of their previous values. The divider ratio alone does not determine how strongly the circuit can drive a load. We investigate that next.

  1. Swap the two resistances. Predict every observed value.
  2. Remove ground as described in chapter 2. Why can the voltage differences remain meaningful while resolution fails?
  3. Explain why replacing a 1 kΩ value with 1[Ohm] is a physical change, while replacing it with 1000[Ohm] is only another way to write the same value.
  4. With R1R_1 fixed and R2R_2 positive, determine the limiting midpoint voltage as R2R_2 becomes very small or very large.
Answer sketches
  1. The total resistance remains 3 kΩ: 4 mA, 4 V, upper power 0.032 W, lower power 0.016 W, source power −0.048 W.
  2. The common potential offset is unconstrained. Ground supplies the missing reference equation; it does not repair a resistor law.
  3. The k prefix multiplies by 1000. Units carry scaling as well as dimensions.
  4. VoutV_{\mathrm{out}} tends to zero as R2R_2 tends to zero, and to VsV_s as R2R_2 grows without bound. These are limits through positive finite values.

Samuel J. Ling, William Moebs, and Jeff Sanny, University Physics Volume 2, OpenStax (2016), §10.2, Resistors in Series and Parallel.

Previous: Conserving networks · Book map · Next: Open the components