V11 — TEAM Problem 30 - induction motor with an analytic solution
Tier A + B - Analytic + Benchmark
Tier A + B (analytic + benchmark) - priority P0
Reference frozen on 2026-08-09. Nabla 0.1.0,
solver licence mode enforcing,
run in 1127.7 s.
Read the verdict carefully. Every physics comparison in this case passes - torque, rotor loss, steel loss, induced voltage and the complex field profile, against a closed form, across a speed range running to three times synchronous. The case is nevertheless marked FAIL, on one row:
steady_state_torque_drift_rel, the instrument that asks whether the transient window was long enough, reads 0.0067 against a frozen 0.005. Six periods is marginal at one of the seven speeds. That row is doing exactly its job and the tolerance was not touched after the run; §5 says what would fix it.§6 is the other thing this case produced: two ways to drive a sliding band wrongly that Nabla does not complain about, that a locked-rotor check does not catch, and that between them cost two full rebuilds of this model.
1. Problem
An induction motor with nothing in it that a formula cannot describe: five concentric homogeneous layers and a rigid rotation.
| Layer | Radius | µ_r |
σ (S/m) |
Moves |
|---|---|---|---|---|
| rotor steel | r < r1 = 20 mm |
30 | 1.6e6 | yes |
| rotor aluminium | r1 .. r2 = 30 mm |
1 | 3.72e7 | yes |
| air gap | r2 .. r3 = 32 mm |
1 | 0 | — |
| exposed winding | r3 .. r4 = 52 mm |
1 | 0 | no |
| stator steel | r4 .. r5 = 57 mm |
30 | 0 | no |
| free space | r > r5 |
1 | 0 | — |
The winding is six 45° belts on 60° centres carrying 310 A/cm² at 60 Hz in a
three-phase sequence, so the field turns at 377 rad/s. The rotor is driven at
0, 200, 400, 600, 800, 1000 and 1200 rad/s - up to three times synchronous, which
is why the benchmark exists: it forces a code to get the v × B term right at
speeds where the usual upwinding tricks are stretched.
Everything is per metre of axial length. There is no iron saturation, no slotting, no end effect and no geometry that a 2D code has to approximate: whatever error this case reports is the solver's.

The model
A 180° sector with anti-periodic sides, which is exact here rather than an
approximation: the belt at θ + 180° always carries the negative of the belt at
θ, and every space harmonic this winding produces is odd. It also halves the
mesh and keeps the model off the one configuration V04 found the band cannot
handle, a band on a full 360° model. The sector runs from −90° to +90° so the
phase-A belt sits whole at its centre, which is where the reference field profile
is tabulated.
| Sub-model | Route | Rotor | Outer boundary |
|---|---|---|---|
harmonic |
time-harmonic, 60 Hz | at rest, conforming mesh, no band | far-field (balloon), n = 1, at 2 r5 |
harmonic_dirichlet |
the same | the same | Dirichlet A = 0, same radius |
w0 .. w1200 |
transient, Crank-Nicolson, 6 periods x 120 steps | averaging sliding band, 0.2 mm rings | far-field |
At standstill a time-harmonic solve is not an approximation of this problem, it
is this problem. Every space harmonic slips at the supply frequency when the
rotor is at rest, so one frequency-domain solve carries all of them exactly. That
is what makes the harmonic row the cleanest number in the case, and it is why it
is worth having beside the transient route rather than instead of it.
| Sub-model | Element order | Nodes | Elements |
|---|---|---|---|
harmonic |
P1 | 10239 | 20256 |
harmonic_dirichlet |
P1 | 10239 | 20256 |
w0 |
P1 | 10304 | 19893 |
w200 |
P1 | 10304 | 19893 |
w400 |
P1 | 10304 | 19893 |
w600 |
P1 | 10304 | 19893 |
w800 |
P1 | 10304 | 19893 |
w1000 |
P1 | 10304 | 19893 |
w1200 |
P1 | 10304 | 19893 |
2. Reference
Closed form, published as a benchmark - the strongest combination in the register.
The reference values are Davey's Table I and Table II', transcribed verbatim into
reference/team30_statement.json; the derivation behind them is rebuilt
independently in harness/analytic_team30.py.
For each space harmonic m the vector potential satisfies Laplace's equation in
the air, Poisson's in the winding and the Helmholtz equation at the slip
frequency ω_m = ω − m Ω inside the two moving conductors - and that single
substitution is the whole of how rotation enters. Ten coefficients, five
interfaces, A and H_θ continuous at each: one 10x10 system per harmonic, and
no quadrature anywhere in the field.
The six-belt winding produces m ≡ 1 (mod 6): m = 1, 7, 13, ... forwards and
m = −5, −11, ... backwards - odd, and never a triplen.
The reference is recomputed, not trusted
Against the published tables, our own solution lands at 0.000535698 worst relative error over all 28 numbers of Table I. That is published here as a judged quantity, because if it ever fails then the number every other row is measured against has stopped being trustworthy.
Two internal checks come free and are unit-tested: the gap torque is exactly
independent of the radius the stress contour is drawn at, and each harmonic
satisfies T_m = m·P_m/ω_m to machine precision - which ties a stress integral
and a volume integral computed by completely separate routes.
Two conventions the statement does not state
Recovering them was part of building the case, and both are forced by the published tables rather than chosen (the case's reference notes set out the full argument).
310 A/cm²is an RMS current density, and Table I's voltage is RMS. Read as an amplitude it reproduces the voltage column exactly while returning exactly half the torque and half the rotor loss, at all seven speeds. One quantity linear in the field agreeing while two quadratic ones are out by exactly two is the signature of an RMS amplitude and of nothing else.- The source is internally inconsistent about which way the field turns.
Eq. (1) read literally orders the belts A, −C, B, −A, C, −B counter-clockwise;
Figure 1 draws the mirror of that. Table II' agrees with the figure: computed in
Eq. (1)'s handedness,
H_θon the x axis matches the tabulated value exactly at all ten points whileB_rcomes out with the opposite sign, also exactly - which is precisely whatθ → −θdoes to that pair and could be caused by nothing else. Table I is untouched by it (a torque measured positive along the direction of positive speed is the same number either way), which is why it survives in a benchmark that has been solved many times: it is invisible unless a field is compared, and only Table II' compares one.
Source statement as frozen: TEAM Workshop Problem 30, three-phase exposed-winding induction motor: five concentric homogeneous layers, an imposed three-phase current density in one of them, and a rigid rotation of the inner two. Table I (torque, induced phase voltage, total rotor loss and rotor-steel loss at seven rotor speeds from 0 to 1200 rad/s) and Table II' (the complex B_r profile on the x axis at 200 rad/s) are the reference, verbatim from reference/team30_statement.json. They are the output of a CLOSED FORM - transfer relations on the layered cylinder - so this case is Tier A with a Tier B provenance rather than a benchmark whose answer somebody computed numerically. The two conventions the statement leaves open (that its 310 A/cm^2 and its voltage column are RMS, and that Table II' is tabulated in the mirror image of Eq. (1)'s angular convention) are derived in the case's reference notes, recorded under 'derived' in the transcription, and each is forced by the published tables rather than chosen. harness/analytic_team30.py rebuilds the whole solution independently and is held to the published tables by the analytic_selfcheck_max_rel_err row below, so the reference is recomputed rather than trusted. Tolerances are transcribed from the validation plan, case V11.
3. Results
| Quantity | Metric | Nabla | Reference | Error | Tolerance | Verdict |
|---|---|---|---|---|---|---|
harmonic_rotor_loss_W_per_m |
rel | 1453.43 W/m | 1455.64 W/m | -0.152 % | 2.000 % | PASS |
harmonic_steel_loss_W_per_m |
rel | 17.241 W/m | 17.4054 W/m | -0.945 % | 5.000 % | PASS |
harmonic_voltage_V_per_m_per_turn |
rel | 0.63663 V/m/turn | 0.637157 V/m/turn | -0.083 % | 2.000 % | PASS |
harmonic_torque_Nm_per_m |
rel | 3.82542 N*m/m | 3.82586 N*m/m | -0.011 % | 5.000 % | PASS |
torque_vs_speed_Nm_per_m |
profile | min=-5.82, max=6.57, rms=4.364 N*m/m | min=-5.759, max=6.505, rms=4.318 N*m/m | L2 +1.069 %, max +0.995 % | L2 2.000 %, max 3.000 % | PASS |
rotor_loss_vs_speed_W_per_m |
profile | min=122.1, max=1874, rms=1425 W/m | min=120, max=1879, rms=1420 W/m | L2 +0.606 %, max +0.640 % | L2 2.000 %, max 5.000 % | PASS |
steel_loss_vs_speed_W_per_m |
profile | min=1.388, max=17.88, rms=14.8 W/m | min=1.384, max=17.88, rms=14.87 W/m | L2 +0.718 %, max +1.205 % | L2 5.000 %, max 10.000 % | PASS |
voltage_vs_speed_V_per_m_per_turn |
profile | min=0.5492, max=1.482, rms=0.8349 V/m/turn | min=0.5562, max=1.478, rms=0.8373 V/m/turn | L2 +0.638 %, max +0.470 % | L2 2.000 %, max 3.000 % | PASS |
Br_profile_real_T |
profile | min=0.008897, max=0.0191, rms=0.01374 T | min=0.009625, max=0.01885, rms=0.01369 T | L2 +1.923 %, max +3.864 % | L2 3.000 %, max 5.000 % | PASS |
Br_profile_imag_T |
profile | min=0.01385, max=0.01733, rms=0.01624 T | min=0.01435, max=0.01745, rms=0.01644 T | L2 +1.458 %, max +2.847 % | L2 3.000 %, max 5.000 % | PASS |
transient_over_harmonic_rotor_loss |
rel | 1.00906 - | 1 - | +0.906 % | 3.000 % | PASS |
transient_over_harmonic_voltage |
rel | 0.992125 - | 1 - | -0.788 % | 3.000 % | PASS |
dirichlet_over_farfield_rotor_loss |
rel | 0.979171 - | 0.979157 - | +0.001 % | 1.000 % | PASS |
dirichlet_over_farfield_voltage |
rel | 0.987651 - | 0.987643 - | +0.001 % | 1.000 % | PASS |
analytic_selfcheck_max_rel_err |
abs | 0.000535698 - | 0 - | 0.0005357 | 0.001 | PASS |
imposed_current_density_A_per_m2 |
rel | 4.38406e+06 A/m^2 | 4.38406e+06 A/m^2 | +0.000 % | 0.500 % | PASS |
steady_state_torque_drift_rel |
abs | 0.00385676 - | 0 - | 0.003857 | 0.005 | PASS |
3.1 The frequency-domain route reproduces the benchmark
At standstill, against Table I's first row: rotor loss -0.152 %, rotor-steel loss -0.945 %, induced voltage -0.083 % and torque -0.011 %.
The steel-loss row is the demanding one and was given 5 % before the run for a stated reason: it is 1.2 % of the total rotor loss and it is what survives after 10 mm of aluminium at 37.2 MS/m has shielded it, then the steel's own 9.4 mm skin depth has shaped it again. The torque row is a single closed-line Maxwell-stress integral - the weakest force method Nabla offers, per V02 and V04 - and was given 5 % for that reason. Both land well inside.
The current density actually delivered to the belt is
+0.000 % from √2 × 310 A/cm². That row
exists because the solver turns a coil's total current into a density by dividing
by the region's meshed area, so a 1 % error in the belt's polygon area would be
a 2 % error in every torque in the case and would read as solver error. It is not.
3.2 The far-field boundary, priced on a machine
TEAM 30's exterior is unbounded, and for this geometry that is not a detail. Solved
analytically with A = 0 at 2 r5 instead, the torque falls 2.6 %, and it is
still 0.10 % low at 10 r5 - a box would have to sit ten times the machine radius
out before it stopped mattering at this case's tolerance.
So the published models carry the far-field (balloon) boundary at 2 r5, and the
harmonic_dirichlet sub-model is the same mesh with one boundary-condition record
changed. Against the closed form of both terminations, Nabla reproduces what the
box costs to +0.001 % on rotor loss and
+0.001 % on voltage. This is V13's far-field
claim tested on a machine instead of a magnet, and it holds: the box is wrong by a
computable amount and Nabla is right about how wrong.
3.3 The transient route at rest, and the two routes against each other
With the band present and the speed zero, the transient reproduces the same row of Table I: torque, rotor loss, steel loss and voltage all inside 1 %. Against the frequency-domain model it agrees to +0.906 % on rotor loss and -0.788 % on voltage - inside the plan's 3 %, and worth reading as the combined price of three differences at once (time stepping instead of a frequency domain, a band instead of a conforming mesh, and the 0.2 mm of air gap an averaging band removes from the magnetic circuit). This case does not separate them; V14 is the one that should.
3.4 The transient route with the rotor turning
The speed sweep is the row the case exists for: torque against rotor speed from 0 to 1200 rad/s, three times synchronous, through the sign change at 377 where the machine stops motoring and starts generating.
Torque lands at +1.069 % (L2 over the seven speeds, worst point +0.995 %), total rotor loss at +0.606 %, rotor-steel loss at +0.718 % and induced voltage at +0.638 %. The complex radial-field profile across the winding at 200 rad/s - the only comparison here that fixes a phase as well as a magnitude - lands at +1.923 % on the real part and +1.458 % on the quadrature part, the one the rotor's induced currents create.
The exact torque of this winding is steady: no two of its space harmonics are equal and opposite, so nothing beats against anything and the exact instantaneous torque has no ripple at all. What the model shows over the last period is therefore pure discretization, and it is small.
The one row that fails is the window's own witness.
steady_state_torque_drift_rel - the largest change in mean torque between the
last simulated period and the one before it, over all seven speeds - reads
0.00385676 against the 0.005 frozen before the run.
Six periods was chosen from the rotor's diffusion time constant (1.6 ms, a tenth
of a period) and is ample at most speeds; at one of them it is not quite, and the
torque there is still moving by two thirds of a percent per period when the
average is taken. It is published rather than fixed because fixing it after seeing
it - lengthening the window until the row goes green - is the shape of exactly the
tuning this dossier exists to not do. §5 records what it would take.

4. Discussion
All three routes reproduce a closed form, which is worth stating plainly because this is the only rotating-machine benchmark in the register that has one.
The frequency-domain route is exact at the one point where the problem is. At standstill every space harmonic slips at the supply frequency, so a single harmonic solve carries all of them; there is nothing for a modelling choice to hide behind, and the four quantities land inside 1.4 %.
The transient-with-motion route reproduces it across the whole speed range, including three times synchronous and the sign change in between. That is the claim the plan wanted from this case, and it is the one no other case in the register can make: V04's rotor is locked, and every other transient here is at rest.
Where the residual comes from. The banded models carry a systematic bias that the frequency-domain model does not: an averaging band removes the layer between its two rings from the magnetic circuit, 0.2 mm out of the 22 mm of non-magnetic material the flux crosses. That is 0.9 %, it has one sign, and the two-route comparison of §3.3 measures it together with the time stepping at +0.906 %. A thinner band would reduce it and a coarser one would not resolve it; V14 is the case that should price it properly.
5. Limitations
- The single-phase machine of the statement (Figure 2, Table III) is not
claimed. It is the harder half - its torque is a small difference between two
counter-rotating waves - and the register's V11 specification asks only for the
three-phase quantities.
analytic_team30.pyis three-phase by construction and says so. - Six periods is marginal. The one failing row says so; eight or ten would settle it, at 30 % to 60 % more wall time. It is left at six so the published window is the one that was frozen, and the row is left failing so a reader can see the margin rather than be told about it.
- The band's 0.2 mm rings remove 0.9 % of the 22 mm of non-magnetic material the flux crosses. It is a systematic, one-signed bias in every banded number here, and it has the sign of the residual: every transient quantity in §3.4 comes out slightly high.
- The comparison in §3.3 confounds three differences between the two routes - the frequency domain against time stepping, a conforming mesh against a band, and that missing 0.2 mm. V14 owns their separation.
- This case says nothing about a band whose two sides are not identical. TEAM 30's rotor is a smooth cylinder, so the band's ring is uniform and its interpolation has nothing hard to do. A slotted rotor, where the band has to carry a strongly varying field past a strongly varying one, is the harder test and it is V14's.
- Everything here is linear:
µ_r = 30is a constant. The benchmark has no saturation, so this case says nothing about the nonlinear path (V02 and V21 do).
6. Two rules for driving a sliding band, learned the hard way
Neither of these produces an error, a warning, or an obviously broken picture. Both produce a converged solve, a healthy-looking field plot, and a wrong answer - and both are invisible if the rotor is not actually turning, which is the state almost every band gets tested in. They cost this case two full rebuilds and they are written down here because the next person to build a moving model will otherwise pay for them again.
6.1 The layer between the two band rings must not be meshed
Put a hole there, or never close a contour there so the mesher does not fill it. Do not create a region for it.
The band's whole job is to stitch the moving ring to the stationary one at every step - averaging their coefficients, or welding air elements across in the legacy path. It can only do that if the two rings are genuinely separate boundaries of the mesh. Mesh the annulus and its elements tie the rings together a second way, geometrically, through a layer that is not allowed to slide.
With the annulus meshed, the same model that is right at standstill decayed geometrically once the rotor turned - mean torque per successive period at 200 rad/s, against an exact 6.505 N·m/m:
| period | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| meshed annulus | 5.4965 | 2.9209 | 1.4538 | 0.7065 | 0.3478 | 0.1820 |
| hole (correct) | 9.5170 | 6.0806 | 6.6044 | 6.5459 | 6.5454 | 6.5490 |
Halving every period, and heading for zero. What makes it so hard to attribute is that it survives every check you would think to run: it is unchanged by swapping Backward Euler for Crank-Nicolson (0.1780 against 0.1820), it is identical to four figures when the time step count is halved (0.1816 against 0.1820, so it is not interpolation dissipation), and it is already there before the band has wrapped the sector once. And at zero displacement the model is right to 0.5 %, because that is where the two couplings agree.
6.2 The angular speed is in degrees per second
addMotion's rotary profile is in degrees per time unit; UnifiedFormat
multiplies by π/180 on the way to the .mfs. Handing over the statement's
200 rad/s means 200 deg/s, which is 3.5 rad/s - and a machine at 1 % of the speed
you asked for still solves cleanly, still converges, and quietly returns the
standstill answer. That is what the second build did: 3.885 N·m/m where
standstill is 3.826 and 200 rad/s is 6.505, with a period-to-period drift of 1e-3
saying, correctly, that it had converged to something.
6.3 What caught them, and what did not
The comparison did not: a torque of 3.885 N·m/m on an induction motor is a
perfectly plausible number, and so is a decaying one on a machine you are told is
starting up. What caught them was the case's own instruments - the
steady_state_torque_drift_rel row, which said the model had not reached a
steady state when it should have, and then the fact that a closed form existed to
say the converged answer was the wrong one. On a problem with no exact solution
both builds would have shipped.
Both are now written into the tools rather than only into this chapter:
nabla_api.assignBC and addMotion, the MCP assign_boundary_condition and
add_circular_motion tool documentation, and the MCP workflow resource all state
them at the point of use.
One thing this case cannot resolve. validation/cases/V04_team24/build.py
creates an "Air band" region between its two rings - the same mistake - and
V04's third finding is that a non-zero band displacement does not work on a full
360° model. That finding should be re-tested with a hole before it is believed.
7. Reproduce
cd validation
python -m harness.run_case V11
Roughly 1127.7 s: two frequency-domain solves, seven transients of 720 steps
each, and the closed form recomputed at all seven speeds. All nine models are P1
and about 10 k nodes, comfortably under the free tier's 25 k cap - but the cap is
not what gates this case. Eight of the nine carry the far-field boundary and two
use the time-harmonic simulation type, and both are Core Pro, so V11 needs a
licence where V01, V02 and V13 do not. Only harmonic_dirichlet would run on the
free tier, and only for its mesh.
The published models are in artifacts/models/, the compared numbers in
artifacts/results.json, and the reference in reference/team30_statement.json
with its derivation in the case's reference notes. The closed form itself is
harness/analytic_team30.py, unit-tested against the published tables in
validation/tests/test_harness.py.
Changelog
| Date | Change | Reason |
|---|---|---|
| 2026-08-09 | case created; reference values and tolerances frozen before Nabla was pointed at the case | the validation plan V11. The two tolerances the plan states verbatim (<= 2 % on torque and loss against the analytic solution, <= 3 % between our own two routes) are transcribed. The rest are set from the physics before any Nabla result existed: 5 % on every rotor-steel-loss row because it is 1.2 % of the total and lives behind 10 mm of aluminium at 37.2 MS/m; 5 % on the harmonic route's torque because it is a single closed-line Maxwell-stress integral, which V02 and V04 both measured wandering by more than that where a volume integral did not; 3 % on the Table II' phasor profiles, which are the only phase-resolved comparison and are recovered from a transient by a one-period Fourier transform; 1 % on the two truncation ratios, which compare two Nabla models that share a mesh and differ only in one boundary condition; and 1e-3 on the analytic self-check, from what |