Skip to content

Total inductance extraction for thin-film superconductors - #929

Open
dnpham23 wants to merge 29 commits into
mainfrom
dnpham23/total-inductance-magnetostatics
Open

dnpham23 wants to merge 29 commits into
mainfrom
dnpham23/total-inductance-magnetostatics

Conversation

@dnpham23

@dnpham23 dnpham23 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds magnetostatic extraction of the total loop inductance (geometric plus London kinetic) for thin-film superconducting structures with holes and current terminals. Flux is imposed as a gauge-invariant fluxoid constraint on each hole, and finite-penetration-depth films carry a London sheet penalty, so the extracted inductance matrix includes the kinetic contribution. Also adds non-conformal AMR support for this path, worked examples, documentation, and regression coverage.

Motivation

Palace's magnetostatics previously produced only geometric inductance. Thin-film superconducting circuits have $d\lesssim\lambda$, where the kinetic inductance is a first-class circuit element. This PR makes Palace extract L = L_geom + L_kin directly.

Examples & docs

  • New examples/circular_hole/ (square washer, single/double circular holes) and examples/microstrip/ (kinetic-inductance validation vs the analytic parallel-plate $\mu_0\lambda_\perp\ell/w$).
  • New docs page Flux Trapping and Kinetic Inductance documenting the formulation and config.

Validation

  • Square washer $(\lambda\rightarrow 0)$: $L\approx 3.04$ pH, matching the Ketchen–Jaycox $1.25 \mu_0 d$ ($\approx 3.14$ pH) formula.
  • Microstrip: kinetic contribution matches the analytic 2-sheet $2\mu_0\lambda_\perp\ell/w$ to ~5%.
  • Regression cases added: square_hole_flux_loop, circular_hole_london_flux, double_hole_london_flux, circular_hole_london_nc_amr, ring_disk_mixed_current_flux; rank-invariant at np=1/2/4/8.

Parse the Superconductor boundary block into SuperconductorData (London
penetration depth and film thickness, or a direct kinetic sheet inductance),
nondimensionalize its lengths in mesh units, extend the config schema, and warn
when it is used outside a Magnetostatic simulation.
Model a thin-film superconductor as a 2D sheet contributing the London kinetic
sheet inductance L_ksq = mu0 * lambda^2 / d. The operator supplies the tangential
surface-mass coefficient (1/L_ksq) A_t . v_t, with per-attribute area scaling for
cracked interior boundaries.
Assemble the superconductor kinetic sheet term into both the base and screened
stiffness matrices so the magnetostatic solve carries the London kinetic
inductance alongside the geometric contribution.
Add the kinetic sheet term to the magnetostatic energy operator M_mag so that
A^T M_mag A = 2 E_mag captures the London kinetic energy, yielding the total
(geometric + kinetic) inductance from the energy-based extraction.
A parallel-plate microstrip loop validating the superconductor sheet against the
closed-form London limits: the PEC baseline gives the geometric inductance, and
the superconductor config recovers L = L_geom + L_kin with L_kin linear in
lambda^2 and the correct lambda -> 0 geometric limit.
…nvergence

The range-space two-solve for London flux loops reused scratch vectors
A_p_london/A_h_london across flux steps without zeroing them. Magnetostatics
defaults to a nonzero initial guess, so a stale gradient/near-null-space
component intermittently stalled CG on the gauge-singular operator
K = K_cc + M_sheet (the fluxoid RHS c = Curlᵀf is marginally conditioned),
producing a garbage A_h whose bad energy was injected via α·A_h -> wildly
wrong extracted inductance on some MPI partitions.

- Zero A_p_london/A_h_london before each solve for a clean, partition- and
  roundoff-independent start.
- Expose KspSolver::GetConverged() and MFEM_VERIFY convergence after both
  London solves, turning a previously silent wrong answer into a hard error.
- Bump ring_disk_mixed_current_flux rtol to 3e-4: its tiny off-diagonal
  mutual is a difference of large self-scale terms, so normal ~1e-6 partition
  FP jitter amplifies to ~1.2e-4 via cancellation (not a partitioning bug).
square_hole_flux_loop and ring_disk_mixed_current_flux declare a FluxLoopPEC
film with no Boundaries.Superconductor block, so they took the whole-film
Dirichlet clamp rather than the range-space two-solve, and both FAIL against
their committed references at the current branch tip (those references encode
the two-solve result). The clamp pins A over the entire film, which is not a
gauge-invariant way to impose the fluxoid.

- Auto-register any magnetostatic FluxLoopPEC attribute that is not already a
  Superconductor as the lambda -> 0 London limit: lambda = d = pec_lperp, so
  L_ksq = mu_0 lambda^2/d = mu_0 pec_lperp. Runs before the ams_singular_op
  default so the film is seen as a London film.
- Add FluxLoop.PecPenetrationDepth to set that effective lambda_perp
  (default 1e-4 mesh units).
- Raise the london_pc_shift default 1e-2 -> 1e-1. On finer meshes 1e-2 leaves
  the harmonic range-space solve intermittently divergent in parallel. The
  shift is preconditioner-only, so the extracted inductance is unchanged.
The hole flux functional c = Curl^T P_RT^T lf was assembled with
VectorFEBoundaryFluxLFIntegrator(1.0), whose sign follows the raw mesh
boundary-face orientation. A hole aperture is an *internal* boundary surface,
and nothing makes those orientations consistent: some generators emit them
mixed, and EnsureNCMesh reorients a subset. c then measured the signed sum of
sub-apertures instead of the whole aperture, so the fluxoid constraint
c^T A = Phi imposed the wrong linear functional.

Fix with a FluxOrientationCoefficient returning
sign(CalcOrtho(T.Jacobian()) . direction), matching the DIRECTION_BASED
convention ComputeFluxThroughSurface already uses.

Verified by Stokes: for A = (-y/2, x/2, 0) (curl A = z_hat), c^T A must equal
the projected aperture area exactly. Measured before the PEC zeroing, the
ratio c^T A / area was

  ring_disk (conformal)          0.101515230  ->  1.000000000
  circular_hole (nonconformal)   0.873673     ->  1.000000000

Note two signals that mask this and should not be trusted alone: the
VerifyFluxThroughHoles printout read 0.99965 for ring_disk despite c being 10x
wrong, and c^T A = Phi always prints exactly Phi, because the two-solve
enforces it by construction.

ring_disk was the affected case, so its references are regenerated (np=2;
mutual 7.973e-12 -> 8.807e-12). The other three London cases were already at
ratio 1.0 and their references are unchanged, which is the useful check: the
fix is a no-op wherever internal faces were already consistently oriented.
The guard rejected *any* flux loop under nonconformal AMR. A London loop's
drive a_h is built directly on the 3D ND space, so it needs no global edge
indexing; only the legacy 2D submesh cohomology path does, via
mesh::MatchBoundaryEdges -> GetGlobalEdgeIndices, which nonconformal meshes
lack. Narrow the guard to non-London loops.

This also unblocks AMR on hex/wedge/pyramid meshes, where geodata forces
nonconformal refinement and the old guard therefore rejected AMR outright.

Nonconformal iteration 1 now reproduces the conformal result to round-off
(c^T A_p 1.158692e-01 vs 1.158693e-01) with the fluxoid exact to ~1e-15 at
every iteration. Known limitation, not addressed here: the +/-Phi cut cochain
is written to local ND_1 DOFs, and slaves are rebuilt as P.(masters), which
deforms the effective cut near hanging faces (iteration 2: 26 DOFs altered,
||delta||/||a_h|| = 9%). Since L depends on a_h restricted to Sigma beyond its
cohomology class, that shifts L non-variationally. Near-uniform nonconformal
refinement agrees with conformal to 0.52%, but highly localized error-driven
nonconformal AMR drives L the wrong way (5.157e-12 -> 4.074e-12 where
conformal gives 5.157 -> 5.648 -> 6.051e-12), because refinement concentrates
on the hole perimeter that the cut crosses. Prefer conformal adaptation for
error-driven AMR until the generator is built on the coarse ancestor and
prolonged through the nested-space ND transfer operator.
Selects the smooth representative a_h = (Phi/2pi) grad(theta),
theta = atan2(y-cy, x-cx), whose exact ND_1 DOF is the angle subtended at the
flux line. It carries the fluxoid exactly (to 4e-15), is bounded, and is
nonconformal-safe, so it is a useful independent cross-check of the default cut
cochain and a one-variable bisection when a result looks wrong.

It is NOT physically interchangeable with the cut cochain, and the comment now
records why so the substitution is not attempted again: the two differ by
grad(psi) with psi = (Phi/2pi) theta_branch, which does not vanish on the PEC
outer walls, so A -> A + grad(psi) is not an admissible gauge transformation of
the Dirichlet-constrained problem and L is not invariant. The large smooth a_h
on Sigma lets the film phase gradient absorb the fluxoid at near-zero kinetic
cost, collapsing the screening current: square_hole comes out 109x low,
circular_hole_london 38x low. Diagnostic use only; default remains the cut
cochain.

Also guards the degenerate case where the flux line meets a hole vertex, where
theta is undefined, by offsetting the line deterministically within the hole.
…gle)

The topological ±Φ step cochain is not a conforming ND field on a
non-conformal mesh: the true-DOF round trip rebuilds slave edges as
P·(masters), deforming the O(1) step near hanging faces and injecting a
large spurious curl on the film Σ. Decompose the generator instead as
a_h = Grad ψ − a_angle, where ψ = (Φ/2π)·atan2(d,−s) is a conformed nodal
H1 branch potential. Grad ψ is a discrete gradient of a conformed function,
so it is exactly conforming and exactly curl-free and survives the round
trip untouched; only the small smooth a_angle term is re-interpolated. On a
conformal mesh this reproduces the exact integer step (all four flux/London
regression cases unchanged); on a non-conformal mesh any residual curl is
confined to a_angle at graded refinement and is absorbed by the range-space
two-solve (verified not to affect L: PEC washer exact and London rect/high-λ
track the conformal reference at matched DOF).

Add circular_hole_london_nc_amr regression case locking the NC generator
path through one non-conformal AMR refinement.
Avoids a reference-root collision with the new circular_hole_london_nc_amr
case: the harness lists reference files recursively, so the flat flux case
picked up the sibling nc_amr/ tree. Isolate each case in its own subdir.
The fluxoid-mode solve A_h = K⁻¹c needs progressively more PCG iterations
as the mesh refines (p-multigrid+AMS is not h-independent on the
gauge-singular operator), so deep AMR eventually hits the Linear.MaxIts cap
and the fail-loud MFEM_VERIFY aborted the whole run. Instead, signal
non-convergence via solve_converged_ and have SolveEstimateMarkRefine stop
adaptation, retaining the last converged iteration's output. The initial
solve still fails loud (no fallback), and non-AMR runs are unaffected.
A pure-PEC (λ→0) flux loop is modeled as a stiff tiny-λ London penalty. On a
deeply refined mesh the linear solve cannot resolve A_t = a_h tightly enough,
so the kinetic penalty energy S(A_t − a_h) = (A − a_h)ᵀ M_sheet (A − a_h) —
which should vanish — becomes large and collapses the extracted inductance
(compounded by catastrophic cancellation in the expanded S(A,A) form).

Form S(A_t − a_h) directly (ApplySheetMass) and, if it jumps by more than 20×
between adaptation iterations, flag the solve so SolveEstimateMarkRefine halts
and keeps the last converged iteration. A converging solve grows this energy
only mildly, so finite-λ films (circular, double, high-λ rect) are unaffected;
verified the PEC square washer now stops at the geometric value (~3.02 pH)
instead of collapsing.
… schema

Drop PALACE_LONDON_AH (incl. the physically-wrong 'angle' mode) and
PALACE_CUT_DIR env switches; the cut cochain a_h = Grad ψ − a_angle is now
the sole generator (cut shape is L-invariant). Add the parsed
FluxLoop.PecPenetrationDepth key to the JSON schema so setting it validates.
Holed PEC sheets always route through the London two-solve (auto-synth),
so the legacy submesh/PEC-clamp path was unreachable. Remove the harmonic_generator
flag, K_orig_ lift, IoData solver copy, and orphaned geodata edge-matching helpers.
@dnpham23 dnpham23 added enhancement New feature or request trigger-long-tests Trigger running long tests for this PR now labels Sep 10, 2026
@dnpham23
dnpham23 marked this pull request as ready for review September 10, 2026 22:57
@dnpham23
dnpham23 requested a review from hughcars September 10, 2026 22:57
@dnpham23 dnpham23 added the push-containers Push the containers produced by CI to ECR and S3 label Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request push-containers Push the containers produced by CI to ECR and S3 trigger-long-tests Trigger running long tests for this PR now

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant