FDM tensor convolution and FFT demagnetization

Last changes: Documentation changelog

The FDM realization represents the non-local demagnetizing operator by a cell-averaged demagnetization tensor and evaluates its convolution with FFTs. It does not solve a scalar potential on an airbox and it does not use FEM boundary matrices. Open-boundary behavior comes from the finite convolution embedding and the precomputed Newell tensor; periodic behavior uses a separate truncated-image policy documented in Periodic Demagnetization.

For multiple separated magnetic layers with native grids, use the dedicated FDM multilayer demagnetizing-field convolution physics page and the complete FDM multilayer convolution: Python, ProblemIR, and UI Python/ProblemIR/UI guide.

1. Physical problem and grid

Let \(i\) and \(p\) index destination and source cells on a uniform Cartesian FDM grid. Each cell has volume \(V\) and magnetization \(\mathbf M_{q,j}=M_s\mathbf m_{q,j}\), where \(q\) is the source-cell index and \(j\) is a Cartesian component. The field in destination cell \(p\) is the discrete cell-volume average of the continuum demagnetizing field generated by all source cells.

The method is naturally non-local: every source cell contributes to every destination cell. FFT embedding changes the computational complexity, not the physical tensor definition. In a multilayer problem, the planner either maps objects to one common grid or preserves per-magnet native grids and performs explicit cross-layer convolutions.

2. Governing equations

The continuous energy convention is

(1)\[E_{\mathrm d}=-\frac{\mu_0}{2}\int_{\Omega_m} \mathbf M\cdot\mathbf H_{\mathrm d}\,\mathrm dV.\]

The cell-averaged tensor \(N^{\mathrm{cell}}_{pq,ij}\) is obtained by integrating the continuum demagnetization kernel over both the source and destination cells. The real-space discrete field is

(2)\[H_{\mathrm d,p,i} =-\sum_q\sum_{j\in\{x,y,z\}} N^{\mathrm{cell}}_{pq,ij}M_{q,j}.\]

The tensor is symmetric in its Cartesian cross-components and is stored as six components: \(N_{xx}\), \(N_{yy}\), \(N_{zz}\), \(N_{xy}\), \(N_{xz}\), and \(N_{yz}\). The convolution kernel computes the raw tensor product without the overall minus sign; the caller applies the sign before inverse FFT and field publication.

The cell energy is

(3)\[E_{\mathrm d} =-\frac{\mu_0}{2}\sum_p V_p \sum_{i\in\{x,y,z\}}M_{p,i}H_{\mathrm d,p,i}.\]

For a uniform grid \(V_p=V\). The factor \(1/2\) removes double counting of the self-interaction. The energy reduction must use the same field, magnetization mask, volume fraction, and precision policy as the field path.

The Newell construction evaluates base functions \(f\) and \(g\) and applies a 27-point stencil. A representative diagonal stencil is

(4)\[N_{xx}(i,j,k)=\frac{1}{4\pi h_xh_yh_z} \left(8f_0-4\sum_{\mathrm{faces}}f+2\sum_{\mathrm{edges}}f-\sum_{\mathrm{corners}}f\right).\]

The implementation uses compensated Kahan–Neumaier summation for the stencil cancellation and stable log1p forms in the base functions. These are numerical-stability measures of the exact cell-averaged tensor construction, not a replacement equation.

The FFT realization embeds the finite grid in a padded grid and uses the convolution theorem:

(5)\[\widehat{\mathbf H}_{\mathrm d}(\mathbf k) =-\widehat{\mathbf N}^{\mathrm{cell}}(\mathbf k)\, \widehat{\mathbf M}(\mathbf k), \qquad \mathbf H_{\mathrm d}=\mathcal F^{-1} \left[\widehat{\mathbf H}_{\mathrm d}\right].\]

Zero padding separates the source and destination copies and prevents circular wraparound from being mistaken for an open-boundary solution. Periodic truncated images intentionally change this embedding and are not the same policy.

3. Symbols and SI units

Symbol

Definition

SI unit

\(\mathbf M\)

magnetization field

\(\mathrm{A\,m^{-1}}\)

\(M_s\)

saturation magnetization

\(\mathrm{A\,m^{-1}}\)

\(\mathbf m\)

reduced magnetization

\(1\)

\(\mathbf H_{\mathrm d}\)

demagnetizing field

\(\mathrm{A\,m^{-1}}\)

\(\mathbf H_{\mathrm{eff},m}\)

full active effective-field sum on magnetic support

\(\mathrm{A\,m^{-1}}\)

\(\mathbf H_{\mathrm{eff},\mathrm{air}}\)

airbox effective-field observable

\(\mathrm{A\,m^{-1}}\)

\(\mathbf H_{\mathrm{ext}}\)

external (Zeeman) field

\(\mathrm{A\,m^{-1}}\)

\(\mathbf H_{\mathrm{oe}}\)

Oersted field

\(\mathrm{A\,m^{-1}}\)

\(\mathbf H_{\mathrm{ant}}\)

antenna field

\(\mathrm{A\,m^{-1}}\)

\(\Omega_m\)

active magnetic support

\(\mathrm{m^3}\)

\(\Omega_{\mathrm{air}}\)

inactive cells of the same FDM structured grid

\(\mathrm{m^3}\)

\(N^{\mathrm{cell}}_{pq,ij}\)

cell-averaged demagnetization tensor

\(1\)

\(N_{xx}\)

diagonal tensor component in the Newell stencil

\(1\)

\(H_{\mathrm d,p,i}\)

component \(i\) of the field in destination cell \(p\)

\(\mathrm{A\,m^{-1}}\)

\(M_{q,j}\)

component \(j\) of source-cell magnetization \(q\)

\(\mathrm{A\,m^{-1}}\)

\(V_p\)

destination-cell volume

\(\mathrm{m^3}\)

\(h_x,h_y,h_z\)

FDM cell sizes

\(\mathrm{m}\)

\(\mathbf k\)

FFT wave vector

\(\mathrm{m^{-1}}\)

\(\mathcal F\)

discrete Fourier transform

\(1\)

\(f\)

Newell diagonal base function

\(\mathrm{m^4}\)

\(g\)

Newell off-diagonal base function

\(\mathrm{m^4}\)

\(E_{\mathrm d}\)

demagnetization energy

\(\mathrm{J}\)

\(\mu_0\)

vacuum permeability

\(\mathrm{N\,A^{-2}}\)

\(p,q\)

destination and source cell indices

\(1\)

\(i,j\)

Cartesian component indices

\(1\)

\(\widehat{\mathbf M}\)

FFT of the packed magnetization

\(\mathrm{A\,m^{-1}}\)

\(\widehat{\mathbf N}^{\mathrm{cell}}\)

FFT of the tensor kernel

\(1\)

\(\widehat{\mathbf H}_{\mathrm d}\)

FFT-domain demagnetizing field

\(\mathrm{A\,m^{-1}}\)

\(\varphi_p\)

magnetic volume fraction of cell \(p\)

\(1\)

4. Assumptions and validity

The standard path assumes Cartesian uniform cells and a magnetization sampled at cell locations. The grid must resolve geometry and the relevant magnetization variation. A cell that is partially magnetic may use a volume-fraction correction, but that correction is a separate configured realization and must be reported.

The following errors are independent:

  • cell discretization and geometry staircasing;

  • finite FFT padding or truncated periodic-image count;

  • Newell tensor construction and self term;

  • FP32/FP64 arithmetic and reduction order;

  • multilayer grid interpolation or cross-layer placement;

  • boundary-correction policy for partial cells.

A matching result between two FFT implementations does not prove continuum accuracy. Validation must compare against an independent analytical or high-resolution reference and must state the grid, padding, precision, mask, and energy reduction policy.

5. Python API and complete parameter reference

Python parameter

Type

Default

SI unit

Validation

Meaning

Backend support

ProblemIR

body.mesh(cell_size=...)

Sequence[float]

required unless a default exists

\(\mathrm{m}\)

Three finite positive values; geometry extents must be exactly divisible.

Native Cartesian cell size of one magnetic object.

FDM CPU/GPU subject to lane qualification.

backend_policy.discretization_hints.fdm.per_magnet.<object>.cell

study.objects.mesh.defaults(cell_size=...)

Sequence[float]

None

\(\mathrm{m}\)

Three finite positive values.

Default native cell size.

FDM CPU/GPU.

backend_policy.discretization_hints.fdm.default_cell

study.universe.mesh(cell_size=...)

Sequence[float]

inferred for compatible grids

\(\mathrm{m}\)

Three finite positive values; common extents must divide exactly.

Requested common convolution-grid resolution for multiple native grids.

FDM multilayer CPU/GPU subject to lane qualification.

backend_policy.discretization_hints.fdm.demag.common_cell_size

FDMDemag.strategy

str

auto

\(1\)

auto, single_grid, or multilayer_convolution.

Selects one common-grid or explicit multilayer convolution topology.

FDM CPU/GPU.

backend_policy.discretization_hints.fdm.demag.strategy

FDMDemag.mode

str

auto

\(1\)

auto, two_d_stack, or three_d.

Selects thin-film stack or full 3-D multilayer mode.

FDM CPU/GPU.

backend_policy.discretization_hints.fdm.demag.mode

FDMDemag.common_cells

tuple[int,int,int] | None

None

\(1\)

Exactly three positive integers.

Explicit 3-D common convolution grid size.

FDM CPU/GPU.

backend_policy.discretization_hints.fdm.demag.common_cells

FDMDemag.common_cells_xy

tuple[int,int] | None

None

\(1\)

Exactly two positive integers.

Explicit in-plane common grid for two_d_stack.

FDM CPU/GPU.

backend_policy.discretization_hints.fdm.demag.common_cells_xy

FDMDemag.allow_single_grid_fallback

bool | None

None

\(1\)

Any non-None value raises ValueError; it is never silently lowered.

Removed compatibility switch.

Unsupported; choose strategy explicitly.

Not serialized

FDMDemag.explain

bool

True

\(1\)

The raw script builder requires a Boolean; the constructor itself does not type-check it.

Enables a human-readable plan summary; it is not serialized physics.

FDM authoring helper.

Not serialized

FDMGrid.cell

tuple[float,float,float]

required

\(\mathrm{m}\)

Exactly three finite positive values.

Native Cartesian cell size for one named magnet.

FDM multilayer authoring.

backend_policy.discretization_hints.fdm.per_magnet.<magnet>.cell

FDM.cell

tuple[float,float,float] | None

None

\(\mathrm{m}\)

Backward-compatible alias; cannot be supplied together with default_cell.

Legacy spelling of the default Cartesian cell size.

FDM CPU/GPU.

Both backend_policy.discretization_hints.fdm.cell and .default_cell

FDM.default_cell

tuple[float,float,float] | None

None

\(\mathrm{m}\)

Exactly three finite positive values; cannot be supplied together with cell.

Default Cartesian cell size.

FDM CPU/GPU.

Both backend_policy.discretization_hints.fdm.cell and .default_cell

FDM.per_magnet

dict[str,FDMGrid] | None

None

\(1\)

Each grid has three positive cell sizes.

Native grid overrides for individual magnets.

FDM multilayer paths.

backend_policy.discretization_hints.fdm.per_magnet

FDM.demag

FDMDemag | None

None

\(1\)

FDM.__init__ has no explicit type check; a valid FDMDemag is required during lowering.

FDM demagnetization topology and common-grid controls.

FDM CPU/GPU.

backend_policy.discretization_hints.fdm.demag

FDM.boundary_correction

str | None

None

\(1\)

none, volume, or full.

Partial-cell correction family: none, T0 volume fraction, or T1 full boundary stencil.

Lane/precision dependent; no universal qualification is implied.

backend_policy.discretization_hints.fdm.boundary_correction

FDM.boundary_phi_floor

float | None

None

\(1\)

Strictly between \(0\) and \(1\) when supplied.

Lower bound for partial-cell volume fraction in stability logic.

Boundary-correction lanes.

backend_policy.discretization_hints.fdm.boundary_phi_floor

FDM.boundary_delta_min

float | None

None

\(\mathrm{m}\)

Greater than or equal to \(0\) when supplied.

Lower bound for T1 boundary distance.

Boundary-correction lanes.

backend_policy.discretization_hints.fdm.boundary_delta_min

Minimal stage-first script

The example below runs one magnet on an FDM CPU FP64 grid and saves H_demag. For multiple disjoint layers, use the complete example on FDM multilayer demagnetizing-field convolution or the FDM multilayer convolution: Python, ProblemIR, and UI guide.

# %% Imports and execution intent
import fullmag as fm

nm = 1.0e-9
study = fm.study("fdm_convolution_python")
study.engine("fdm")
study.device("cpu", precision="double")
study.mode("strict")
study.interactive(False)

# %% One magnetic body and physical terms
study.universe(
    mode="manual",
    size=(32.0 * nm, 16.0 * nm, 8.0 * nm),
    center=(0.0, 0.0, 0.0),
    padding=(0.0, 0.0, 0.0),
)
magnet = study.geometry(fm.Box(size=(24.0 * nm, 12.0 * nm, 4.0 * nm)), name="magnet")
magnet.mesh(cell_size=(4.0 * nm, 4.0 * nm, 4.0 * nm))
magnet.Ms = 800.0e3
magnet.Aex = 13.0e-12
magnet.alpha = 0.02
magnet.m = fm.init.UniformMagnetization((1.0, 0.0, 0.0))
study.exchange(enabled=True)
study.demag(enabled=True)

# %% Observable and fixed-step stage
study.save("H_demag", every=1.0e-13)
study.solver(integrator="rk4", fix_dt=1.0e-14, gamma=2.211e5)
study.stages.add_run(until=1.0e-13, stage_id="fdm_demag_run")

6. ProblemIR and normalization

FDMDemag.to_ir() stores the requested strategy and mode, plus explicit common-grid sizes. The outer FDM.to_ir() stores cell sizes, per-magnet overrides, boundary controls, and the nested demag policy. The planner then resolves actual grid dimensions, FFT padding, precision, masks, periodicity, and memory estimates. None of these resolved values may be inferred from auto after execution; they belong in provenance.

The interaction term Demag() enables demagnetization. FDMDemag does not replace the physical term; it controls the FDM numerical realization. This separation means a requested physical model can remain identical while the resolved single-grid and multilayer paths differ.

7. Round-trip and failure semantics

Requested intent contains the authored cell, strategy, mode, boundary correction, and precision request. Resolved execution contains the common or native grids, padded FFT shape, tensor spectrum identity, active masks, periodic boundary policy, FP32/FP64 lane, memory reservation, and runtime identity.

Constructor validation errors cover invalid strategies, modes, cell shapes, non-positive cell sizes, malformed common-grid dimensions, invalid volume-fraction floors, and the removed fallback switch. Planner validation reports incompatible periodic policies, grid-cost overflow, missing validated tensor spectra, and illegal output requests. Unsupported combinations are reported explicitly; the planner must not silently change multilayer convolution to one common grid or replace CUDA with CPU.

8. Discrete realization by lane

FDM CPU reference

The CPU reference path computes or consumes Newell tensor spectra, packs the magnetization into the FFT domain, accumulates the symmetric six-component tensor product, applies the overall negative sign, inverse-transforms the field, and performs the energy reduction. It is a trusted numerical reference lane, not evidence that a CUDA device executed.

FDM GPU FP64

The persistent CUDA FP64 path uploads prevalidated tensor spectra, uses cuFFT for the vector components, applies device-side mask and volume-fraction semantics, and reduces energy in a CUDA reduction path. FP64 is a separate arithmetic lane: host double precision and device execution identity must both be recorded.

FDM GPU FP32

The FP32 path uses float magnetization/field and complex spectra. It has distinct rounding and reduction behavior from FP64. A source-level or compile-only check cannot establish FP32/FP64 parity; the same grid, tensor, magnetization, padding, and executed device must be used in a comparison.

Terminal single-grid fields and airbox — planned / in implementation

This section specifies a terminal runtime contract; it does not add a Python DSL parameter, ProblemIR property, or a new demagnetization API. The contract is planned / in implementation until fresh completed-runtime evidence exists. In particular, source-level code, unit contracts, or CPU execution do not qualify a CUDA device path.

On active magnetic support, H_eff is the full sum of every resolved active contribution:

(6)\[\mathbf H_{\mathrm{eff},m} = \sum_{\ell\in\mathcal A_m}\mathbf H_\ell, \qquad \mathbf x\in\Omega_m.\]

The airbox is an observable domain, not magnetic solver support. When all source fields below are defined and materialized from the same state, it exposes only:

(7)\[\mathbf H_{\mathrm{eff},\mathrm{air}} = \mathbf H_{\mathrm d} + \mathbf H_{\mathrm{ext}} + \mathbf H_{\mathrm{oe}} + \mathbf H_{\mathrm{ant}}, \qquad \mathbf x\in\Omega_{\mathrm{air}}.\]

For each of H_demag, H_ext, H_oe, and H_ant, the terminal legality check must record full_domain coverage for this exact structured grid, state generation, step, and time. Matching vector length does not prove this: a field can be magnetic_only. If a source is absent in the resolved plan its contribution is zero. If a required source is not full_domain or is not materialized from the same state, the airbox aggregate is unavailable; it must not combine values from different steps. H_demag is full_domain by the FDM observable contract. H_ext is full_domain only when its own observable metadata says so. H_oe is full_domain only when its own observable metadata says so. H_ant is full_domain only when its own observable metadata says so; its support cannot be inferred from its name. Material-only fields (exchange, anisotropy, DMI, magnetoelastic, thermal and torque) are zero or unavailable in the airbox with explicit metadata.

For Completed, the target publication is one atomic replacement batch: final m and every requested field carry exactly the same terminal step and time. A newer complete batch replaces the previous terminal batch as a whole; consumers must not merge old and new quantities. This is session-memory retention only, not disk persistence or a restart guarantee. Durable field output still requires an explicit output/artifact schedule. Failed and Cancelled runs do not publish a completed terminal batch; any retained earlier batch keeps its own status, step, and time.

disable_preview_3d controls intermediate 3-D preview work for benchmark/display use. It neither requests omission of terminal scientific-field finalization nor proves that a field has been persisted. The CPU single-grid target is host materialization and atomic replacement. The CUDA single-grid target must publish the same complete batch with executed-device evidence, or reject an unsupported requested terminal quantity explicitly; it may not silently substitute CPU data.

Multilayer convolution

single_grid makes the planner construct one common grid. multilayer_convolution preserves per-magnet native grids and evaluates self- and cross-layer tensor products explicitly. In two_d_stack, common in-plane cells are mandatory when explicitly provided; thickness and layer placement remain part of the resolved plan. The path must not silently fall back when the requested layout cannot be represented.

9. Implementation mapping

Responsibility

Stable source owner

Lane

Python policy

class FDMDemag

Public authoring

Newell base functions and 27-point tensor

compute_newell_kernels

CPU/reference preparation

FFT tensor product

accumulate_tensor_convolution

CPU/reference

CPU field path

compute_newell_kernel_spectra and demag_field_from_vectors

FDM CPU

CUDA FP64 field

launch_demag_field_fp64

FDM GPU FP64

CUDA FP32 field

launch_demag_field_fp32

FDM GPU FP32

CUDA FP64 energy

reduce_demag_energy_fp64

FDM GPU FP64

GPU resource/FFT setup

context_upload_demag_kernel_spectra and context_refresh_demag_observable

FDM GPU

Active and airbox effective-field reconstruction

reconstruct_inactive_fdm_visual_effective_field

FDM CPU, source-level owner; terminal contract not runtime-qualified

Atomic terminal field materialization

build_atomic_terminal_update

FDM CPU, publishes one terminal update with the final field set

CPU terminal outcome

execute_reference_fdm

FDM CPU source-level current path; not proof of an atomic terminal generation/batch

Session field promotion

ingest_preview_fields_from_update

CLI promotes fields individually; not proof of an atomic terminal generation/batch

10. Validation and qualification

Validation

Required evidence

Newell self term

Symmetry, diagonal trace convention, and independently computed self interaction.

Open boundary

Padding and far-field comparison against an analytical or high-resolution reference.

Uniform body

Demagnetization factors and energy for a rectangular prism, sphere, or ellipsoid.

CPU/GPU parity

Same tensor spectrum, grid, mask, precision, reduction policy, and executed device.

FP64/FP32 comparison

Absolute and relative field/energy error with a stated scale; near-zero values need absolute tolerances.

Multilayer

Self and cross-layer terms, layer offsets, common-grid policy, and no-fallback behavior.

Boundary correction

T0/T1 geometry, volume fractions, distance floor, precision and lane-specific evidence.

11. Limitations

FFT padding is a finite computational embedding, not a proof of exact continuum infinity. FP32 reductions can differ from FP64 even when both use the same tensor spectrum. A larger FFT grid may reduce wraparound error but increases memory and does not fix geometric staircasing. rtol and linear-solver parameters belong to FEM Poisson/BEM; FDM convolution has no Krylov solve and must not expose those parameters as if they controlled FFT accuracy.

12. Scientific bibliography

  1. A. J. Newell, W. Williams and D. J. Dunlop, “A calculation of the demagnetizing factors for cylinders,” Journal of Geophysical Research, 98, 9551–9555 (1993).

  2. W. F. Brown, Micromagnetics, Wiley, 1963.

  3. FullMag internal reference: docs/physics/0420-fdm-dipolar-demag-foundations.md.

13. Source-code index

Repository path

Stable symbol

Responsibility

Lane

packages/fullmag-py/src/fullmag/model/discretization.py

class FDMDemag

Strategy, mode, common-grid and explanation policy.

Public authoring

packages/fullmag-py/src/fullmag/model/discretization.py

class FDM

Cell, multilayer and boundary-correction policy.

Public authoring

crates/fullmag-fdm-demag/src/newell.rs

compute_newell_kernels

Newell base functions, 27-point tensor and stable summation.

CPU/reference

crates/fullmag-fdm-demag/src/multiply.rs

accumulate_tensor_convolution

Symmetric tensor-vector FFT product before negation.

CPU/reference

crates/fullmag-engine/src/fdm/cpu/fft.rs

compute_newell_kernel_spectra

CPU kernel-spectrum construction.

FDM CPU

crates/fullmag-engine/src/fdm/cpu/fields.rs

demag_field_from_vectors

CPU field realization.

FDM CPU

backends/fdm/gpu/cuda/interactions/demag_fp64.cu

launch_demag_field_fp64

CUDA double-precision field dispatch.

FDM GPU FP64

backends/fdm/gpu/cuda/interactions/demag_fp32.cu

launch_demag_field_fp32

CUDA single-precision field dispatch.

FDM GPU FP32

backends/fdm/gpu/cuda/runtime/reductions_fp64.cu

reduce_demag_energy_fp64

CUDA FP64 energy reduction.

FDM GPU FP64

backends/fdm/gpu/cuda/runtime/context.cu

context_upload_demag_kernel_spectra

Device tensor-spectrum upload and validation.

FDM GPU

backends/fdm/gpu/cuda/runtime/context.cu

context_refresh_demag_observable

Device demag observable refresh.

FDM GPU

crates/fullmag-runner/src/fdm/cpu/reference.rs

reconstruct_inactive_fdm_visual_effective_field

Reconstructs the defined airbox aggregate from demag, external, Oersted and antenna fields.

FDM CPU, source-level only

crates/fullmag-runner/src/interactive/runtime.rs

build_atomic_terminal_update

Builds one atomic terminal update and materializes the final FDM field set from the terminal backend state.

FDM CPU, source-level only

crates/fullmag-runner/src/fdm/cpu/reference.rs

execute_reference_fdm

Returns the CPU terminal run outcome and scheduled outputs; it is not proof of an atomic terminal generation/batch.

FDM CPU, source-level only

crates/fullmag-cli/src/live_workspace.rs

ingest_preview_fields_from_update

Promotes incoming fields individually into session state; it is not proof of an atomic terminal generation/batch.

CLI control plane

crates/fullmag-cli/src/live_workspace.rs

feature_flags

Reads preview-disable benchmark/display configuration.

CLI control plane

Control Room crosswalk

Use Model Explorer -> Objects -> <object> -> Physics when PhysicsInteractionPanel exposes the interaction. Status: partial. frontend support is not implemented applies to physical parameters without a matching control. See {doc}/frontend/capability-register; do not infer UI support from backend or Python availability.

Python/API crosswalk

The linked Python API page is authoritative for exact functions, arguments, units, and failure semantics. If this page is a foundation or category overview, runnable Python is ot applicable here and must be taken from the terminal API page.

Bibliography and source scope

Use the scientific bibliography and source-code index on the linked terminal page. This block adds no new equation or unverified implementation claim.

Source-code index

  • Public Python and lowering sources are linked by the applicable terminal API page. Runtime realization is in the relevant backends/fdm or backends/fem lane; frontend ownership is apps/control-room/src/modules/inspector/panels/PhysicsInteractionPanel.tsx where a live control exists.