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:$PYTHONPATHQuick 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, JoulesFor 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")