Grid-free magnetostatic oracle + fix cancelling multilayer coils#692
Merged
Conversation
…coils Two independent pieces of the exact-oracle work for motor certification. ## add_coil multilayer was electromagnetically wrong Stacked layers reused one spiral, so series current ran outer->inner on even layers and inner->outer on odd ones through identical copper. Consecutive layers circulated opposite ways and their axial fields cancelled: a 4-layer stack produced roughly the field of one layer, and an even stack roughly nothing. Confirmed numerically first -- the new test reconstructs the current path from board data (chaining traces, following stitch vias) and found per-layer dipole moments alternating -,+,-,+. The stack is now one continuous spiral in angle: theta marches monotonically across every layer while the radius zigzags inner<->outer, with a stitch via at each turnaround. Circulation is constant, so fields add, and consecutive layers meet at the identical (r, theta) so vias align by construction. Also fixes a pre-existing hole collision (not introduced here): with an integer turn count every inner turnaround landed on the same spoke, stacking two vias in one hole (HoleToHole -0.400mm). Successive turnarounds are now fanned by a stagger angle sized from via drill plus the hole-to-hole minimum at the binding inner radius. Tests cover 2/4/8-layer stacks for sign consistency, ~linear moment scaling, and DRC-clean. ## vcad-kernel-magnetostatic Air-core machines have no iron, so mu = mu_0 everywhere, the problem is linear, and superposition is exact. That collapses the usual FE machinery into a sum over current segments: closed-form Biot-Savart for B, closed-form integral of A.dl for flux linkage. No mesh, no iteration, no truncation boundary. Validated four independent ways: infinite-wire limit, on-axis loop closed form, off-axis loop against vcad-kernel-particle's elliptic integrals (a different crate written for a different purpose), and curl A = B by finite differences, which ties the two separately-derived integrals together. Back-iron via method of images. Real "air-core" machines are only air-core in the winding region; steel back-irons close the circuit and are worth ~2.4x in airgap flux for the reference geometry (65 mT free space -> 106 mT one plane -> 155 mT full circuit), so ignoring them is not a small error. Unsaturated mild steel is mu_r >~ 1000, making the mu -> infinity mirror accurate to ~0.1%. Measured precondition, documented and enforced: the two-plane image series converges only for a magnetically balanced source set. A lone magnet between two infinite plates never settles (7.9e-4 -> 2.2e-5 T over depths 2..32); a 6-pole alternating rotor converges to 6e-5 by depth 16. Real multi-pole rotors are balanced by construction, but balance_residual() enforces it and tail_fraction() reports the achieved truncation rather than assuming it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Choji has used up today's reviewsYour team has reached the free plan's limit of 5 reviews per day, so Choji stepped aside on this pull request. That's a plan limit, not a reflection of the change itself. Your free reviews reset tomorrow. To have Choji review every push without waiting, upgrade to Pro for unlimited reviews. You're on the free plan · 5 reviews/day. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…rrents A uniformly magnetized body is exactly equivalent to K_b = M x n on its surface. For a prism magnetized along z that vanishes on the flat faces and reduces, traced around the side walls, to a closed loop following the footprint. So any axially-magnetized magnet -- disc, arc, rectangle -- becomes a stack of loops around its outline, each carrying M*dz, feeding the same segment integrator as the coils. One representation covers every magnet shape a machine uses. Validated against the closed form for an axially magnetized cylinder (within 2% on axis), plus polarity antisymmetry and winding-order invariance. Anchors the solver-path decision numerically: a Y30 D15x3 ferrite disc gives ~65 mT at 1 mm in free space, and adding the rotor back-iron lifts that by 1.4-2.1x toward the ~155 mT the reluctance model predicts for the full circuit -- which is why an iron-free oracle cannot grade this machine. MagnetRing::discs builds an alternating-polarity rotor, verified magnetically balanced so the two-plane image series converges. Fidelity: exact for uniform M with recoil permeability 1. Real sintered magnets are mu_rec ~ 1.05 (NdFeB) to 1.1 (ferrite), so working flux is over-predicted by 5-10% -- the dominant magnet-side error, documented rather than hidden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r-oracle # Conflicts: # Cargo.lock
ecto
added a commit
that referenced
this pull request
Jul 25, 2026
The same PR (#692) also trips clippy 1.95's manual_is_multiple_of, which is the next gate after the format check — so main fails clippy too, and fixing only the formatting just moved the red one step down the job. `k % 2 == 0` -> `k.is_multiple_of(2)` on usize: identical behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ecto
added a commit
that referenced
this pull request
Jul 25, 2026
* style(magnetostatic): rustfmt the crate `Grid-free magnetostatic oracle` (#692) landed unformatted, so `cargo fmt --check` fails on main and every open PR is red for a reason that has nothing to do with its own diff. Pure `rustfmt --edition 2021` output, no hand edits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(magnetostatic): satisfy clippy::manual_is_multiple_of The same PR (#692) also trips clippy 1.95's manual_is_multiple_of, which is the next gate after the format check — so main fails clippy too, and fixing only the formatting just moved the red one step down the job. `k % 2 == 0` -> `k.is_multiple_of(2)` on usize: identical behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First step of the certified-motor work: an exact field oracle to grade
calc_motor's closed form against, plus a real bug it surfaced.add_coilmultilayer was electromagnetically wrongStacked layers reused one spiral, so series current ran outer→inner on even layers and inner→outer on odd ones through identical copper. Consecutive layers circulated opposite ways and their axial fields cancelled — a 4-layer stack made roughly one layer's field, an even stack roughly nothing.
Confirmed numerically before touching it: the new test reconstructs the current path from board data (chaining traces, following stitch vias) and found per-layer dipole moments alternating
−,+,−,+.The stack is now one continuous spiral in angle — θ marches monotonically across every layer while the radius zigzags inner↔outer, with a stitch via at each turnaround. Circulation is constant so fields add, and consecutive layers meet at the identical (r, θ) so vias align by construction.
Also fixes a pre-existing hole collision (present in the old code, not introduced here): with an integer turn count every inner turnaround landed on the same spoke, stacking two vias in one hole (
HoleToHole −0.400mm). Successive turnarounds are now fanned by a stagger angle sized from via drill + hole-to-hole minimum at the binding inner radius.Tests cover 2/4/8-layer stacks for sign consistency, ~linear moment scaling, and DRC-clean.
vcad-kernel-magnetostaticNo iron ⇒
μ = μ₀everywhere ⇒ linear ⇒ superposition is exact. That collapses the usual FE machinery into a sum over current segments: closed-form Biot-Savart forB, closed-form∮A·dlfor flux linkage. No mesh, no iteration, no truncation boundary — and fast enough to sit inside a design search later.Validated four independent ways:
vcad-kernel-particle's elliptic integrals — a different crate, written for a different purpose∇×A = Bby finite differences, tying the two separately-derived integrals togetherBack-iron by method of images
Real "air-core" machines are only air-core in the winding region; steel back-irons close the magnetic circuit. For the
examples/pcb-motorgeometry that steel is worth ~2.4× in airgap flux (65 mT free space → 106 mT one plane → 155 mT full circuit), so ignoring it is not a small error. Unsaturated mild steel isμ_r ≳ 1000, so theμ→∞mirror is accurate to ~0.1%.Measured precondition, now documented and enforced: the two-plane image series converges only for a magnetically balanced source set.
Real multi-pole rotors are balanced by construction, but
balance_residual()enforces it andtail_fraction()reports achieved truncation rather than assuming it.Scope, stated honestly
flux_density_estimate()surfaces the working iron flux so the assumption is checkable.Lis less trustworthy thanKt.Note for the roadmap
examples/pcb-motorwas never physically built — its "verified numbers" (B_gap 0.155 T,Kt 3.7 mN·m/A) are kernel-computed, not measured; only the clearances are measured. So this oracle can grade the closed form, but the third measured column needs an actual build and dyno.Test plan
cargo test -p vcad-kernel-magnetostatic— 14 testscargo run -p vcad-kernel-magnetostatic --example image_convergence --releasenpx vitest runinpackages/mcp— 934 tests, no regressions🤖 Generated with Claude Code