net.netclass
the tool-assigned net class a net belongs to (KiCad net_settings; not the derived semantic role)
What it is ¶
net.netclass(net, class) yields one row per net that the design tool put in a named class, with the
class string recorded verbatim: Default, Power, HighSpeed, whatever the project declares. A net
left in the tool’s implicit default carries no class and produces no row, so not net.netclass(?n, ?_)
reads as “unclassed”.
The name is deliberate. net.netclass is the tool-assigned label; it is not net.class, which
would belong to the derived semantic role space that net.ground and the ir.Net.roles stamp
(WS3-072) occupy. The two answer different questions. A net in the Power class is one an engineer
put there; a net that satisfies net.ground is one the naming lexicon read as ground. A rule that
conflated them would join against nothing and report clean.
For hardware engineers ¶
A net class is how you tell the layout tool that a group of nets shares rules: these are the high-speed pairs, these are the high-voltage nets, these carry more current than the default track width allows. It is the near-universal scope expression in vendor rule decks, and it is the label you already maintain in the project rather than something the engine guesses from a name.
Query it to confirm the engine sees the classes you assigned, and to scope a review question the way
you would scope a design rule: “which parts sit on an HV net”, “is any high-speed pair missing its
termination”. The class is assignment, not measurement: a net in HighSpeed is one someone declared high-speed,
not one the engine verified is routed that way.
A net can be in several classes at once, and the engine reports all of them. If you assigned VBUS
to both Power and HighCurrent, it shows up under both, and a query scoped to either one finds it.
That matters when you are checking coverage: a net missing from a scope you expected it in is a real
finding, and it used to be possible for the engine to lose the membership rather than report it.
For software engineers ¶
A filtered projection over Nets(), 1:1 with classed nets and absent for the rest. It joins to
everything else keyed by net name (component-on-net, pin.net, net.max_voltage), so it composes as
a scope filter on any existing question.
?net is NOT unique in this projection. Membership is a set, so a net in two classes emits two
rows and a join on ?net fans out, the same 1:many shape component.class has. Count rows and you
are counting memberships, not nets; net.netclass(?n, ?c) => ?n on a design where half the nets carry
two classes returns more results than the design has classed nets.
WS1-050 settled this against the formats rather than against our first reader. KiCad stores
map<netname, set<netclass>> and resolves a net’s membership by unioning its explicit assignment with
EVERY matching pattern, then cascades the per-class VALUES by priority to build one effective class.
Altium likewise lets a net join several classes, though its clearance matrix treats that as an
ambiguity to flag rather than a feature. Same data model, opposite house reading, so the set
is carried as fact here and whether a second class is a problem is left to a rule.
Order is sorted, and that is a determinism guarantee only. It is NOT the tool’s precedence order: in
KiCad precedence is a per-class priority that lives with the class DEFINITIONS (clearance, track
width, via), which nothing reads yet (WS3-111). Precedence decides whose track width wins, never who
is a member, so it cannot change the answer to a membership question.
The value is a foreign label, not a closed enum: it comes from the project file, so string comparisons are exact and case-sensitive, and two projects can use different vocabularies for the same intent. Do not treat an unrecognized class as an error, and do not derive meaning from the string beyond what the project declares.
Go projector ¶
netNetClassFacts in stdlib/relations/facts.go walks Model.Nets() and emits a row per entry of
each net’s NetClasses. The field is populated in the I/O layer, not by any analysis:
readers/formats/registry.go reads net_settings.netclass_{assignments,patterns} out of the sibling
.kicad_pro and calls kicad.AnnotateNetClasses (WS1-037). One row per (net, class) pair; zero rows
when the design has no classes, the common case, and the reason the companion marker below
exists.
Do not populate ir.Net.net_classes from IPC-2581. Its LogicalNet/@netClass is spelled the same but
means something else: a singular CLOSED enum (CLK/FIXED/GROUND/SIGNAL/POWER/UNUSED)
describing what a net IS. That is the derived-role space net.ground and ir.Net.roles occupy, not
user-named constraint groups, and mixing the two would put GROUND and HighSpeed in one relation
where a class-scoped query would silently select the wrong nets.
Absence is not a pass ¶
Only a KiCad project read populates net class. An EDIF netlist, an IPC-2581 board, a bare
.kicad_sch opened without its project, and a KiCad project that simply declares no classes all leave
this relation empty. A rule SCOPED by net class then selects nothing, finds nothing, and reports clean
and a review cannot tell that from a genuine pass. That is the false-pass family (WS3-090 / 096 / 097 /
098 / 099) reached by a new route: not an empty datasheet join and not a requirement that compiles to
nothing, but a scoping relation that is empty because the source carries no such data.
The route this relation takes is the capability gate. A netclass-scoped rule declares
check.CapNetClass, and check.Available reports it not-applicable, with the reason “design carries
no net-class assignments (only a KiCad project file supplies them)”, wherever the design assigns no
classes. The gate is content-derived, not format-derived, because for a scoped rule “this project
declares no classes” and “this format has no classes” are the same answer: there is nothing in scope
either way. has_netclass is the queryable twin of that capability, so an ad-hoc query can ask whether
a class-scoped question is even answerable on this design before trusting its result.
Datalog ¶
Every classed net and its class:
net.netclass(?net, ?class) => ?net, ?class
Scope an existing question by the project’s own class, the way a vendor rule deck does, over the parts sitting on a high-speed net:
net.netclass(?net, "HighSpeed"), component-on-net(?ref, ?net) => ?ref, ?net
Ask the honest version, which returns nothing on a design with no classes rather than a clean-looking empty result:
has_netclass(?_), net.netclass(?net, "HighSpeed"), component-on-net(?ref, ?net) => ?ref, ?net