Skip to content

units: Ohms.__str__ uses engineering notation (kΩ, MΩ, GΩ) - #33

Merged
raeq merged 2 commits into
mainfrom
ohms-engineering-notation
May 20, 2026
Merged

units: Ohms.__str__ uses engineering notation (kΩ, MΩ, GΩ)#33
raeq merged 2 commits into
mainfrom
ohms-engineering-notation

Conversation

@raeq

@raeq raeq commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

The previous Ohms.__str__ used a bare :.3g formatter, producing scientific output for clean kilo / mega multiples that no schematic or bench builder would actually write:

value before after
10 000 1e+04 Ω 10 kΩ
100 000 1e+05 Ω 100 kΩ
1 000 000 1e+06 Ω 1 MΩ

The breadboard SVG visualiser (#28) surfaced the problem — a resistor body labelled 100000Ω makes the reader count zeros. Centralising the engineering notation in Ohms.__str__ means every downstream format (assembly guide, breadboard SVG, kicad netlist, BOM, reports) inherits the readable form.

  • Ohms.__str__ returns 47 Ω, 4.7 kΩ, 10 kΩ, 100 kΩ, 1 MΩ, 10 MΩ, 1 GΩ.
  • Resistor.__str__ delegates to str(self._ohms) instead of rolling its own raw-float format.
  • The breadboard renderer uses str(part.ohms) directly (its private compact-format helper is gone).
  • tests/components/test_resistor.py::test_str updated (47 Ω, was 47.0 Ω); new parametrised test_str_renders_in_engineering_notation pins kΩ / MΩ output across the thresholds.
  • All affected demo docs / goldens refreshed (breadboard SVGs, kicad sch value fields, BOM rows, reports).

Test plan

  • uv run pytest — 4383 passed.
  • uv run mypy src/ demos/ clean.

The previous `Ohms.__str__` formatted with bare `:.3g`, producing
unreadable scientific output for clean kilo / mega multiples:

  str(Ohms(10_000))     == "1e+04 Ω"      → now "10 kΩ"
  str(Ohms(100_000))    == "1e+05 Ω"      → now "100 kΩ"
  str(Ohms(1_000_000))  == "1e+06 Ω"      → now "1 MΩ"

The breadboard SVG visualiser surfaced the problem: a resistor body
labelled "100000Ω" makes a reader count zeros, while a label of
"100 kΩ" reads at a glance — exactly the convention every schematic
and parts catalogue uses. Centralising the engineering notation in
`Ohms.__str__` means every downstream consumer (assembly guide,
breadboard SVG, kicad netlist, BOM, reports) inherits the readable
form without each format adapter rolling its own formatter.

  - `Ohms.__str__` returns "47 Ω", "4.7 kΩ", "10 kΩ", "100 kΩ",
    "1 MΩ", "10 MΩ", "1 GΩ" per the kilo / mega / giga thresholds.
  - `Resistor.__str__` delegates to `str(self._ohms)` so it
    inherits the new notation (was producing the noisy "47.0 Ω"
    via raw float).
  - The breadboard visualiser's resistor body label uses
    `str(part.ohms)` directly (previously had a private
    `_format_ohms_compact` helper; removed).
  - `tests/components/test_resistor.py::test_str` updated for
    "47 Ω" (was "47.0 Ω"); new `test_str_renders_in_engineering_notation`
    pins kΩ / MΩ output across the kilo and mega thresholds.
  - All demo docs / goldens refreshed where the Ohms.__str__
    change affected the rendered output (breadboard SVGs, kicad
    sch value fields, BOM rows).
Copilot AI review requested due to automatic review settings May 19, 2026 20:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves resistance string rendering across the codebase by switching Ohms.__str__ to canonical engineering notation (e.g., 10 kΩ, 1 MΩ) so exports (breadboard SVG, KiCad schematics, BOM/report outputs) show human-friendly resistor values.

Changes:

  • Update Ohms.__str__ to emit engineering notation for kΩ/MΩ/GΩ thresholds.
  • Simplify Resistor.__str__ and breadboard SVG rendering to delegate to Ohms.__str__.
  • Refresh tests and golden/demo artifacts to match the new formatting.

Reviewed changes

Copilot reviewed 15 out of 27 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/framework/units.py Implements engineering-notation formatting in Ohms.__str__.
src/components/passives/resistor.py Delegates Resistor.__str__ to Ohms.__str__.
src/framework/export/breadboard/renderer.py Renders resistor body labels using str(part.ohms) (engineering notation).
tests/components/test_resistor.py Updates __str__ expectations and adds coverage for kΩ/MΩ formatting.
tests/golden/kicad_sch/water_alarm.kicad_sch Refreshes KiCad schematic golden to use engineering notation.
tests/golden/breadboard/water_alarm.breadboard.svg Refreshes breadboard SVG golden resistor value labels.
tests/golden/breadboard/hello_led.breadboard.svg Refreshes breadboard SVG golden resistor value labels (adds space / Ω formatting).
tests/golden/breadboard/doorbell_protector.breadboard.svg Refreshes breadboard SVG golden resistor value labels (kΩ/MΩ).
tests/golden/breadboard/digital_thermometer.breadboard.svg Refreshes breadboard SVG golden resistor value labels (adds space / Ω formatting).
tests/golden/breadboard/dice.breadboard.svg Refreshes breadboard SVG golden resistor value labels (adds space / Ω + kΩ).
demos/water_alarm/docs/WaterAlarm.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/water_alarm/docs/WaterAlarm.breadboard.svg Refreshes demo breadboard SVG resistor labels.
demos/penfold_one_second_timer/docs/OneSecondTimer.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/penfold_one_second_timer/docs/OneSecondTimer.breadboard.svg Refreshes demo breadboard SVG resistor labels.
demos/li_ion_fuel_gauge/docs/BatteryPackBoard.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/hello_led/docs/HelloLED.breadboard.svg Refreshes demo breadboard SVG resistor label formatting.
demos/fan_cooling/docs/FanCoolingBoard.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/fan_cooling/docs/CooledSystem__FanCoolingBoard.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/doorbell_protector/docs/DoorbellProtector.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/doorbell_protector/docs/DoorbellProtector.breadboard.svg Refreshes demo breadboard SVG resistor labels (kΩ/MΩ).
demos/digital_thermometer/docs/DigitalThermometer.breadboard.svg Refreshes demo breadboard SVG resistor label formatting.
demos/dice/docs/Dice.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/dice/docs/Dice.breadboard.svg Refreshes demo breadboard SVG resistor label formatting.
demos/bldc_motor/docs/BLDCSystem__BLDCControllerBoard.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/bldc_motor/docs/BLDCControllerBoard.kicad_sch Refreshes demo KiCad schematic values to engineering notation.
demos/backup_power/docs/BackupPower.kicad_sch Refreshes demo KiCad schematic values to engineering notation (including non-integer kΩ).
demos/5v_rail_power/docs/FiveVoltRailPower.breadboard.svg Refreshes demo breadboard SVG resistor label formatting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +67 to +77
def test_str_renders_in_engineering_notation():
"""Resistor.__str__ delegates to Ohms.__str__, which uses
canonical engineering notation (kΩ, MΩ) rather than raw ohms.
A schematic / bench builder reads `10 kΩ` instantly; `10000 Ω`
forces them to count zeros."""
assert str(Resistor(ohms=1_000, refdes_number=1)) == "1 kΩ"
assert str(Resistor(ohms=4_700, refdes_number=2)) == "4.7 kΩ"
assert str(Resistor(ohms=10_000, refdes_number=3)) == "10 kΩ"
assert str(Resistor(ohms=100_000, refdes_number=4)) == "100 kΩ"
assert str(Resistor(ohms=1_000_000, refdes_number=5)) == "1 MΩ"
assert str(Resistor(ohms=10_000_000, refdes_number=6)) == "10 MΩ"
Same treatment as `Ohms.__str__` (previous commit) for the other two
passive engineering quantities. Demos contain caps and inductors
with values that the bare `:.3g` formatter would emit in scientific
form (`1e-07 F` for a 100 nF decoupling cap); centralising the
engineering notation on the unit class means every consumer
(reports, BOM, breadboard SVG, etc.) inherits the readable form.

  - `Farads.__str__` returns `100 pF`, `22 nF`, `100 nF`, `1 µF`,
    `4.7 µF`, `470 µF`, `1 mF`, `1 F` across the standard
    capacitance bands.
  - `Henries.__str__` returns `100 nH`, `10 µH`, `22 mH`, `1 H`
    across the standard inductance bands.
  - `Capacitor.__str__` and `Inductor.__str__` delegate to the
    unit class's str (were rolling raw-float formatters).
  - `framework.export.reports_common.part_description` now uses
    `str(part.ohms / farads / henries)` directly instead of the
    parallel `_format_ohms` / `_format_farads` / `_format_henries`
    helpers (removed — they duplicated the new logic on raw
    floats).
  - Breadboard SVG renderer now also labels capacitor and inductor
    bodies with their engineering-notation value (was only
    resistors).
  - `tests/framework/test_units.py`: parametrised regression tests
    for both unit classes across every band.
  - All affected demo docs / goldens refreshed.
@raeq
raeq merged commit 38087ab into main May 20, 2026
3 checks passed
@raeq
raeq deleted the ohms-engineering-notation branch May 20, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants