Getting Started

Building the Python Module#

Ferrolith requires a C++17 compiler, CUDA (tested with 12.5), and NetCDF. All other dependencies (Eigen, H2Lib, AMGCL, pybind11) are fetched automatically by CMake.

cmake -B build -DFERROLITH_PYTHON=ON
cmake --build build -j$(nproc)

To manually set the GPU architecture:

cmake -B build -DFERROLITH_PYTHON=ON \
    -DFERROLITH_FIND_CUDA_ARCH=OFF \
    -DFERROLITH_SET_CUDA_ARCHS="61;75"

The compiled module is placed in build/python/. Add it to your Python path:

export PYTHONPATH=build/python:$PYTHONPATH

Quick Start#

The simplest way to compute a demagnetizing field is with DemagFieldFK:

import numpy as np
from ferrolith import DemagFieldFK

# Load your mesh (n nodes, ntet tetrahedra)
coordinates = ...  # (n, 3) node positions
elements = ...     # (ntet, 4) tetrahedral connectivity, 0-indexed

# Material parameters
Ms = np.full(n, 800e3)   # saturation magnetization (A/m)
scale = 1e-9              # length unit: nanometres

# Create the solver
demag = DemagFieldFK(coordinates, elements, Ms, scale=scale)

# Uniform magnetization along z
m = np.zeros((n, 3))
m[:, 2] = 1.0

# Compute effective field and energy
H = demag.compute(m)      # (n, 3), returns mu0 * Ms * H_d
E = demag.energy(m)       # scalar, Joules

For a uniformly magnetized sphere with $M_s = 800{,}000$ A/m, the demagnetizing factor is $N = 1/3$, giving $H_z \approx -\mu_0 M_s^2 / 3 \approx -268{,}083$.

Composing Multiple Fields#

For micromagnetic problems you typically need several field contributions. There are three high-level compositors, one for each demag pipeline:

  • EffectiveFieldFK – Fredkin-Koehler (BEM-based, configurable backend)
  • EffectiveFieldShell – shell transform (no BEM, requires zoned mesh)
  • EffectiveFieldVolFMM – direct volume FMM (no BEM, no FK decomposition)

All three share the same add_*() / set_*() / compute() interface. Here is the FK variant:

from ferrolith import EffectiveFieldFK, SolverType

eff = EffectiveFieldFK(coordinates, elements, Ms, scale=scale)

# Add field components
eff.add_demag(solver=SolverType.AMG_CG)
eff.add_exchange(A)         # A: (n,) exchange stiffness in J/m
eff.add_anisotropy(Ku, axis) # Ku: (n,) in J/m^3, axis: (n, 3)
eff.add_zeeman(Hext)        # Hext: (n, 3) in A/m

# Total effective field and energy
H = eff.compute(m)
E = eff.energy(m)

# Per-component access
H_demag = eff.compute_component("demag", m)
E_demag = eff.energy_component("demag", m)

The volume FMM variant is similar but takes no BEM parameters:

from ferrolith import EffectiveFieldVolFMM

eff = EffectiveFieldVolFMM(coordinates, elements, Ms, scale=scale)
eff.add_demag()
eff.add_exchange(A)

Updating Parameters#

Material parameters can be changed without rebuilding the solver from scratch:

# Update Ms and exchange stiffness
eff.set_Ms(Ms_new)
eff.set_exchange(A_new)
eff.recompute()   # rebuild affected components

# Zeeman field updates are lightweight (no recompute needed)
eff.set_zeeman(Hext_new)

BEM Backends#

The default BEM backend is GPU FMM. Other backends can be selected for validation or when H2Lib caching is preferred:

from ferrolith import DemagFieldFK, BemBackend, ParamsFKH2Lib

# GPU FMM (default) -- fully GPU-accelerated
demag = DemagFieldFK(coords, elems, Ms, scale=scale)

# H2Lib -- CPU H2-matrix with optional caching
demag = DemagFieldFK(coords, elems, Ms, scale=scale,
                   backend=BemBackend.H2LIB,
                   h2_cache="sphere.h2")

# Dense BEM -- O(n^2) exact reference for small meshes
demag = DemagFieldFK(coords, elems, Ms, scale=scale,
                   backend=BemBackend.DENSE_BEM)

FMM Tree Types#

For geometries with high aspect ratios (e.g. thin films), the FMM tree type affects compression quality:

from ferrolith import DemagFieldFK, TreeType

# Adaptive octree -- non-cubic bounding boxes
demag = DemagFieldFK(coords, elems, Ms, scale=scale,
                   tree_type="adaptive")

# k-d tree -- binary splits along longest axis
demag = DemagFieldFK(coords, elems, Ms, scale=scale,
                   tree_type="kdtree")