DEV Community

HVAC Fitter
HVAC Fitter

Posted on

Modeling "will this part fit?" as code: a replacement-rule validator for HVAC capacitors

Most "does this part fit?" questions in e-commerce get answered with a lookup table: part A replaces part B because somebody typed it into a spreadsheet. That breaks down for commodity electrical parts, where thousands of part numbers are really just a handful of ratings. For those, it's better to encode the substitution rules and check any candidate against the original part.

Run capacitors in air conditioners and heat pumps are a clean example. The label on the old part tells you nearly everything you need, and the rules for a safe replacement are short:

  1. Capacitance (µF, also printed MFD) must match exactly. A dual-run label like 45 + 5 MFD means 45 µF for the compressor (HERM) and 5 µF for the fan. The tolerance printed next to it (±5% or ±6%) is the normal manufacturing range. It doesn't let you pick a different size.
  2. Voltage must be the same or higher. A 440 V part can replace a 370 V part. A 370 V part can never replace a 440 V one.
  3. Type must match. Dual run (three terminals: HERM, FAN, C), single run (two terminals) and start capacitors (rated as a range, like 88 to 108 µF) aren't interchangeable.
  4. Shape doesn't matter electrically. Round vs oval is only a fit-in-the-bracket question.

Here's that as a tiny validator:

from dataclasses import dataclass

@dataclass(frozen=True)
class Capacitor:
    kind: str            # "dual_run", "single_run" or "start"
    uf: tuple            # (45, 5) for dual, (10,) for single
    volts: int           # 370 or 440, typically
    shape: str = "round"

def can_replace(original: Capacitor, candidate: Capacitor) -> list[str]:
    """Return a list of problems. An empty list means it's a valid substitute."""
    problems = []
    if candidate.kind != original.kind:
        problems.append(f"type mismatch: {candidate.kind} vs {original.kind}")
    if candidate.uf != original.uf:
        problems.append(f"µF must match exactly: {candidate.uf} vs {original.uf}")
    if candidate.volts < original.volts:
        problems.append(f"voltage too low: {candidate.volts} V < {original.volts} V")
    if candidate.shape != original.shape:
        problems.append("note: different shape, check the mounting bracket")
    return problems

old = Capacitor("dual_run", (45, 5), 370)
print(can_replace(old, Capacitor("dual_run", (45, 5), 440)))   # []
print(can_replace(old, Capacitor("dual_run", (40, 5), 440)))   # µF mismatch
print(can_replace(Capacitor("dual_run", (45, 5), 440),
                  Capacitor("dual_run", (45, 5), 370)))        # voltage too low
Enter fullscreen mode Exit fullscreen mode

A few design notes from building this kind of check:

  • Return reasons, not a boolean. "Doesn't fit" is useless to a customer. "Voltage too low" teaches them the rule.
  • Separate hard failures from warnings. Shape is a warning. µF and voltage are hard stops.
  • Parse the label, not the part number. Many brands print the same ratings under different numbers. Normalizing 45/5, 45+5 and 45 + 5 MFD into one tuple does most of the work.
  • Never let tolerance leak into matching. It's tempting to treat ±6% as a fuzzy-match window. Don't.

If you want the plain-English version of these rules (with label photos and the safety steps, because a capacitor can hold a charge with the power off), the HVAC Fitter capacitor guide is where I took them from. A worked example of one rating is the 45/5 MFD dual run capacitor spec page.

The same pattern works for contactors (coil voltage and amps), transformers (VA and volts) and air filters (nominal size). Encode the rule once and every new part number is just data.

Safety note: this is about data modeling. If you're replacing a capacitor yourself, turn off power at the breaker and the outdoor disconnect and discharge the capacitor first, or call a licensed HVAC technician.

Top comments (0)