BEM¶
Backend lightsmith-bem (method bem, modules lightsmith.backends.bem and lightsmith.backends._bem). A
boundary element (surface integral equation) solver for scattering by
spheres, spheroids, cylinders, layered spheres and clusters of disjoint particles in a homogeneous medium.
It runs on the CPU or a CUDA GPU.
Only the particle surfaces are meshed, so curved metal boundaries converge smoothly instead of being staircased by a volume grid. For a gold sphere of radius 40 nm the error against Mie is below 0.1 % with 540 unknowns (see Convergence).
Method¶
- Formulation. PMCHWT surface integral equations (Kern & Martin 2009): equivalent electric and magnetic currents J and M on every interface, expanded in RWG functions on flat triangles (Rao, Wilton & Glisson 1982). The formulation covers several regions, so each interface of a layered sphere carries its own J and M.
- Singular integrals. For nearby triangle pairs, the 1/R and ∇(1/R) parts of the Green's function are integrated analytically over the source triangle (Graglia 1993) and the quadrature of the same terms is subtracted. These corrections do not depend on the wavelength and are computed once per mesh.
- Meshes. Geodesic spheres, arc-length-mapped spheroids, cylinders with graded rims, concentric spheres for layered particles, and any number of disjoint particles. By default each faceted surface is rescaled to the true volume of the shape (radially only for cylinders, whose flat ends are exact). This removes the leading O(h²) error of the inscribed polyhedron.
- Linear solve. Dense LU, or restarted GMRES with Jacobi preconditioning on the dense matrix.
- Cross sections.
- C_ext comes from the optical theorem, −Re(bᴴx), whose error is quadratic in the current error;
- C_sca is the far-field power flux over a Gauss–Legendre sphere;
- C_abs = C_ext − C_sca.
The Poynting flux through the particle surface is reported in provenance["c_abs_flux_nm2"] as an
independent check only: with RWG currents it converges slowly.
- Fields. Far fields (dC_sca/dΩ) and near fields, inside and outside the particles. Near the surface
the static parts of the field integrals are integrated analytically.
- Orientation average. Fibonacci-sphere incidence directions, each with two polarisations (the same set
as DDA). With the direct solver the matrix is factorised once per wavelength for all of them.
The matrix assembly uses the array API, so the same code runs on NumPy (CPU) and PyTorch (CPU or CUDA).
Options¶
options["bem"] or options["lightsmith-bem"].
| Option | Default | Meaning |
|---|---|---|
mesh_size_nm |
None |
Target mean triangle edge. Default: the smaller of size / elements_per_size and λ_min / (|n|_max · elements_per_wavelength). |
elements_per_size |
10 |
Edges across the smallest particle dimension (for layered spheres, also twice the thinnest shell) |
elements_per_wavelength |
10 |
Edges per wavelength in the densest material |
volume_correction |
True |
Rescale each faceted surface to the true volume |
quadrature |
7 |
Points of the triangle quadrature rule: 3, 7 or 12 |
near_factor |
2.0 |
Triangle pairs closer than this times the sum of their circumradii get analytic singularity extraction |
solver |
"auto" |
"direct" (dense LU), "gmres", or "auto": direct up to direct_max_unknowns |
direct_max_unknowns |
20000 |
Switch-over point for "auto" |
tol |
1e-8 |
GMRES relative residual target |
restart |
100 |
GMRES restart length |
max_iter |
3000 |
GMRES iteration cap |
precision |
"double" |
"single" halves memory and is faster on consumer GPUs; conditioning worsens on very fine meshes |
max_unknowns |
60000 |
Refuse problems with more unknowns (2 × edges) |
orientations |
32 |
Incidence directions (× 2 polarisations) for orientation_average |
assembly_budget |
None |
Kernel evaluations per assembly chunk. Default 2e6 on the CPU, 8e6 on a GPU. |
There are no gradients. estimate reports the mesh (triangles, edges, unknowns), the solver, the number of
linear solves and the memory, which is dominated by the dense matrix (16 bytes × unknowns² in double
precision).
Validation¶
All tests are in tests/test_bem.py. Materials are Johnson–Christy gold and silver, in water unless stated.
Errors are relative errors of the cross sections.
| Test | Reference | Tolerance |
|---|---|---|
test_dielectric_sphere_matches_mie: n = 1.5 + 0.02i, r = 40 nm, h = 12 nm |
Mie | C_ext, C_sca, C_abs < 0.2 % |
test_metal_sphere_matches_mie_on_and_off_resonance: Au and Ag, r = 40 nm, h = 12 nm |
Mie | C_ext, C_abs < 0.3 %; no warnings |
test_mesh_convergence_without_volume_correction |
Mie | error falls by at least 30 % from h = 14 to 9 nm, and is < 3 % at 9 nm |
test_layered_sphere_matches_mie: Au core 30 nm + 10 nm n = 1.45 shell |
Mie | C_ext, C_abs < 0.5 % |
test_far_and_near_field_match_mie: Au r = 30 nm, h = 8 nm |
Mie | dC_sca/dΩ 0.2 %; |E|² 3 % away from the surface |
test_far_field_integrates_to_c_sca |
own C_sca | 0.1 % |
test_orientation_average_of_sphere |
fixed incidence | 0.2 % |
test_gmres_matches_direct |
dense LU | 1e-7 |
test_spheroid_sphere_limit_matches_mie: spheroid with equal semi-axes |
Mie | C_ext < 0.3 % |
test_spheroid_polarization_anisotropy: Drude 16 × 48 nm rod |
physics | longitudinal peak > 80 nm to the red of the transverse one |
test_dielectric_shapes_agree_with_dda: 20 × 40 nm spheroid, 20 × 60 nm cylinder |
DDA, 24 dipoles per diameter | C_ext < 3 % |
test_dimer_couples_and_is_symmetric: two Au r = 20 nm spheres |
single sphere; mirror image | bonding mode red-shifted and > 2× stronger at a 5 nm gap; 2 × single sphere within 3 % at a 1500 nm gap; mirror symmetric to 1e-6 |
test_gpu_matches_cpu (gpu) |
CPU | double 1e-9; single 2e-3 (measured about 2e-4) |
test_gold_sphere_fine_mesh_converges_below_1e4 (slow): h = 6 nm, 520–800 nm |
Mie | C_ext, C_abs < 3e-4 |
test_gold_rod_converges_with_mesh (slow): 20 × 60 nm Au rod, h = 4, 3, 2 nm |
self-convergence | band-integrated C_abs: the error falls with h and is < 3 % at h = 3 nm |
test_small_rod_approaches_quasistatic_limit (slow): 10 × 30 nm Drude rod, h = 1.5 nm |
quasistatic with radiative correction | same peak wavelength; band-integrated C_abs within 3 % (measured 1 %) |
The test module states the measured accuracy behind these tolerances: C_ext and C_abs within about 0.05 % of Mie at a 12 nm mesh on a 40 nm gold or silver sphere, on and off the plasmon resonance.
Convergence¶
benchmarks/bem_convergence.py solves a gold (Johnson–Christy) sphere of radius 40 nm in water at 520, 600,
700 and 800 nm on a ladder of mesh sizes. The error is the maximum over the four wavelengths of
|C/C_Mie − 1|. Results committed in benchmarks/results/20261101-bem-convergence/ (RTX 5070):
mesh_size_nm |
Unknowns | C_ext error | C_abs error | Without volume correction (C_ext) |
|---|---|---|---|---|
| 16 | 540 | 0.056 % | 0.055 % | 11.7 % |
| 12 | 960 | 0.032 % | 0.033 % | 6.79 % |
| 10 | 1500 | 0.021 % | 0.022 % | 4.42 % |
| 8 | 2160 | 0.015 % | 0.016 % | 3.09 % |
| 6 | 3840 | 0.0081 % | 0.0091 % | 1.76 % |
| 4 | 8640 | 0.0035 % | 0.0040 % | 0.785 % |
Without volume correction the error falls as h². The same run solves the sphere with DDA up to
64 dipoles per diameter and FDTD down to 2 nm cells; their largest errors over the four
wavelengths are in summary.md of that directory. The wall times there were measured on a shared, loaded
machine and are indicative only.
Limits¶
- No substrate: BEM uses the homogeneous-space Green's function. Use DDA or FDTD.
- Particles must be disjoint; overlapping or touching particles are refused.
- Near and far fields are not available together with
orientation_average. - Raises an error above
max_unknowns(increasemesh_size_nm). The dense matrix limits practical meshes to some tens of thousands of unknowns. - Warns when GMRES does not reach
tol, and when the currents are not finite (tryprecision="double"or a finer mesh). - Thin rods need about 10 edges across their diameter (the default
elements_per_size). Thetest_small_rod_approaches_quasistatic_limitdocstring records that at h = 2 nm the peak of a 10 nm diameter rod is still 4 nm to the blue.