Skip to content

Replace FFTW with fortnum fft_c2c and drop the FFTW dependency (4/5) - #32

Open
krystophny wants to merge 7 commits into
mainfrom
drop-gsl-fortnum-fftw
Open

Replace FFTW with fortnum fft_c2c and drop the FFTW dependency (4/5)#32
krystophny wants to merge 7 commits into
mainfrom
drop-gsl-fortnum-fftw

Conversation

@krystophny

@krystophny krystophny commented Jun 14, 2026

Copy link
Copy Markdown
Member

Merge order

Stack sequence: #29 -> #30 -> #31 -> #32 -> #33.

Each PR is now based on main individually, so its diff is cumulative against main and shares code with its stack neighbors. Merge in the sequence above. Once a predecessor merges into main, the next PR rebases onto it and its diff shrinks to its own increment.


Fourth in the fortnum migration stack (base: the C-quadrature PR). Removes MEPHIT's FFTW usage.

Dependency removed: FFTW. The fft_t one-dimensional forward transform in src/mephit_util.f90 moves onto fortnum's fft_c2c, which matches the FFTW sign convention (sign = -1, unnormalized). The type now owns an allocatable samples buffer instead of FFTW-allocated pointers and an opaque plan; apply transforms a copy in place so the caller's samples survive, then extracts and normalizes the requested mode range exactly as before. src/fftw3.f90, the cmake/FindFFTW.cmake module, and the empty FFTW_LIBRARIES link entry are removed. find_package(FFTW) was never invoked in this repo, so FFTW_LIBRARIES expanded to nothing and the FFTW symbols from src/fftw3.f90 were unresolved at executable link time; dropping that source fixes the link.

Verification

Full build at this tip, including the executables that did not link on the earlier tips and on main:

$ cmake -S . -B build -G Ninja && cmake --build build
[27/30] Linking CXX shared library lib/libmephit.so
[28/30] Linking C executable bin/mephit_run.x
[29/30] Linking Fortran executable bin/mephit_post.x
[30/30] Linking Fortran executable bin/mephit_test.x

MEPHIT references no FFTW symbol and uses fortnum's transform instead:

$ nm -DuC build/lib/libmephit.so | grep -ic fftw
0
$ nm -DC build/lib/libmephit.so | grep -i fft_c2c
0000000000120020 T __fortnum_fft_MOD_fft_c2c
$ ldd build/bin/mephit_run.x | grep -i fftw || echo "no fftw in mephit_run.x deps"
no fftw in mephit_run.x deps

Before this PR the executable link failed:

$ cmake --build build
/usr/bin/ld: lib/libmephit.so: undefined reference to `fftw_plan_dft_1d'
/usr/bin/ld: lib/libmephit.so: undefined reference to `fftw_execute_dft'
...
collect2: error: ld returned 1 exit status

Note: fortnum pin updated to current main (92de6e9) after a fortnum history rewrite; old shas no longer resolve.

Drop the gsl_sf_bessel_In_array C binding in mephit_pert and compute the
modified Bessel functions through fortnum's bessel_in_array. fortnum fills
I_0..I_nmax in one pass, so the orders |m|-1, |m|, |m|+1 are recovered with
the symmetry I_{-n}(x) = I_n(x); for pol_mode = 0 the order -1 maps to I_1.

Wire fortnum via CMake FetchContent at the pinned tag, guarded so a build that
also pulls fortnum transitively through libneo does not declare the target
twice. Add the fortnum include directory for the C/C++ sources and link the
fortnum target. GSL stays linked until the remaining call sites move over.
@krystophny
krystophny force-pushed the drop-gsl-fortnum-c-quadrature branch from fa2e524 to 6d6cc8b Compare June 14, 2026 09:33
@krystophny
krystophny force-pushed the drop-gsl-fortnum-fftw branch from 1c548fc to 73bfe4c Compare June 14, 2026 09:33
Route the a = 1 confluent hypergeometric evaluation through the fortnum C ABI:
the boundary integral uses fortnum_integrate_qag with the Gauss-Kronrod 21
rule, and the Kummer-series acceleration uses fortnum_levin_u_accel. Both
fortnum routines take the integrand plus an opaque context, so the existing
quad_params struct is forwarded unchanged and no workspace is allocated. Add
float.h for DBL_EPSILON, which was previously pulled in transitively through a
GSL header.
Move the remaining C-side GSL call sites onto the fortnum C ABI. The
Gauss-Legendre nodes and weights on the unit interval now come from
fortnum_gauss_legendre_ab, which folds the table allocation and the [a, b]
mapping into one call. Drop gsl_errno_msg and the gsl_set_error_handler
registration: fortnum routines report failures through return codes rather than
a global GSL error handler, so the dedicated GSL message hook and its forward
declaration are no longer reachable. After this change libmephit.so resolves no
GSL symbols.
Move the fft_t one-dimensional forward transform onto fortnum's fft_c2c, which
matches the FFTW sign convention (sign = -1, unnormalized). The type now owns an
allocatable samples buffer instead of FFTW-allocated pointers and an opaque
plan; apply transforms a copy in place so the caller's samples survive, then
extracts and normalizes the requested mode range exactly as before. Remove
src/fftw3.f90, the cmake/FindFFTW.cmake module, and the empty FFTW_LIBRARIES
link entry. MEPHIT no longer references any FFTW symbol; libmephit.so and the
executables link without FFTW.

@slopqueue slopqueue Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict: Request changes

Here's where I landed after the full review:

PR #32 Review: Drop GSL/FFTW → fortnum

Verdict: Approve

Correctness — all verified

Change Old New Status
FFT fftw_plan_dft_1d + FFTW_FORWARD (sign=-1, unnormalized) fft_c2c(spectrum, -1) in-place on copy ✓ Same sign, same /N normalization, same mode extraction
Bessel I_n gsl_sf_bessel_In_array(|pol|-1, |pol|+1, ...) bessel_in_array(|pol|+1, ...) + manual I_{-n}=I_n symmetry ✓ Verified for pol_mode = 0, 1, 2+
Gauss-Legendre gsl_integration_glfixed_table_alloc on [0,1] fortnum_gauss_legendre_ab(order, 0, 1, ...) ✓ Same interval, same order
QAG quadrature key=GSL_INTEG_GAUSS21 (=2), limit=100 key=21 (fortnum convention), limit defaults to 500 ✓ Same GK rule; 500 > 100 = more permissive, not a regression
Levin accel gsl_sum_levin_u fortnum_levin_u_accel ✓ Same interface
gsl_set_error_handler Custom handler _exit(1) in forked child Removed — no GSL calls remain in any source file ✓ Safe

Key findings

  1. GSL::gsl is now a dead dependency — still linked in MEPHIT_LIBS but zero GSL calls remain in any .c/.f90/.F90/.h file. find_package(GSL REQUIRED) should be removed in a follow-up.

  2. FFTW_LIBRARIES was always emptyfind_package(FFTW) was never called; FindFFTW.cmake existed but was never invoked. The old code linked FFTW only because the linker found it in system paths. The PR replaces this fragile setup with a properly pinned FetchContent of fortnum.

  3. allocatable replaces raw pointers in fft_t — safer, no manual fftw_alloc_complex/fftw_free, no c_associated checks. No memory leaks.

  4. CI only builds, doesn't testmake test target exists but .github/workflows/main.yml only runs make. Pre-existing, not introduced by this PR.

  5. fft_t type is public but unused — no other file in the repo imports it. Pre-existing dead code.

No blocking issues. The PR is correct, well-scoped (-538/+47 lines), and improves build reliability.

@slopqueue slopqueue Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict: Approve

I have all the evidence. The gh token is expired (HTTP 401), so I can't post the review to GitHub directly. Here is the complete review:


PR #32 Review: Replace FFTW and GSL with fortnum

Verdict: Approve with minor issues

The numerical changes are all correct. The algorithms match, sign conventions match, output ordering matches. Three minor cleanup items should be addressed but are not blockers.

Changes Reviewed

File Change Verification
hyper1F1.c GSL QAG + Levin-u → fortnum_integrate_qag / fortnum_levin_u_accel ✓ Correct
mephit_fem.c GSL Gauss-Legendre → fortnum_gauss_legendre_ab ✓ Correct
mephit_pert.f90 GSL Bessel I_n → fortnum_special::bessel_in_array ✓ Correct
mephit_util.f90 FFTW → fortnum_fft::fft_c2c; fft_t simplified to allocatable ✓ Correct
mephit_run.c gsl_set_error_handler(gsl_errno_msg) removed ✓ Acceptable
mephit_util.c/.h gsl_errno_msg deleted ✓ Clean
CMakeLists.txt FFTW removed, fortnum FetchContent added ✓ Correct
cmake/FindFFTW.cmake Deleted (419 lines)
src/fftw3.f90 Deleted

Numerical Correctness (all verified against fortnum source at pinned commit 92de6e9)

1. QAG integration — hyper1F1.c:61,64

fortnum_integrate_qag(&exp1mt, 0.0, 1.0, 1e-12, 1e-12, 21, f_re, &err, &qp) matches old gsl_integration_qag with GSL_INTEG_GAUSS21:

  • GK21 rule: fortnum's xgk21/wgk21/wg21 arrays match GSL's standard Gauss-Kronrod 21-point nodes/weights exactly (verified at fortnum_integrate_gk.f90:55-72).
  • Callback: exp1mt(double, void*) matches fortnum_scalar_fn = double (*)(double, void*). Context &qp passed as ctx (old: F.params = &qp). ✓
  • limit: default 500 (old: 100). More subintervals → tighter convergence possible. Not a correctness issue.
  • Status: return value discarded. Old code also discarded gsl_integration_qag's return value. No regression.

2. Levin-u acceleration — hyper1F1.c:295,302

fortnum_levin_u_accel(t_re, N, f_re, &err) matches gsl_sum_levin_u_accel:

  • u-variant: beta = 1, omega_n = (beta + n) * a_n (Weniger 7.3-9). Same as GSL.
  • Column recurrences: qnum(j) = qnum(j+1) - fact*qnum(j) with fact = ((beta+n)/(beta+n+k))^(k-1). Matches GSL's recurrence exactly.
  • Best-order selection: minimum error estimate over all diagonals. Same as GSL.
  • Print change: N (total terms) replaces w->terms_used (terms actually used). Cosmetic; the accelerated value f_re/f_im comes from the function, not the print. ✓

3. Gauss-Legendre — mephit_fem.c:234

fortnum_gauss_legendre_ab(order, 0.0, 1.0, points, weights) replaces gsl_integration_glfixed_point:

  • Newton's method for P_n roots (16 iterations, convergence 4*eps).
  • [0, 1] mapping: x = 0.5 + 0.5*x, w = 0.5*w. Same affine map as GSL.
  • Nodes/weights match to floating-point precision. ✓

4. Bessel I_n — mephit_pert.f90:1418-1428

bessel_in_array(|pol_mode|+1, k_z_r, I_n) fills I_n(0..|pol_mode|+1) = I_0..I_{|pol_mode|+1}:

  • pol_mode = 0: I_m(-1) = I_n(abs(-1)) = I_n(1) = I_1. Old GSL: I_{-1} = I_1 (symmetry). ✓
  • pol_mode = 1: I_m(-1) = I_n(0) = I_0, I_m(0) = I_n(1) = I_1, I_m(1) = I_n(2) = I_2. ✓
  • pol_mode ≥ 2: all three indices map correctly via I_n(abs(|pol_mode|-1)), I_n(|pol_mode|), I_n(|pol_mode|+1). ✓
  • Array bounds I_n(0:|pol_mode|+1) match bessel_in_array output size. No out-of-bounds. ✓

5. FFT — mephit_util.f90:395-410

fft_c2c(spectrum, -1) replaces fftw_execute_dft(plan, samples, modes):

  • Sign: -1 = forward DFT = FFTW_FORWARD. exp(-2πi·j·k/n). ✓
  • Unnormalized: fortnum returns raw DFT (no 1/N). MEPHIT normalizes by / dble(fft%N). Same as FFTW. ✓
  • In-place on copy: spectrum = fft%samplesfft_c2c(spectrum, -1) → read spectrum. fft%samples survives. ✓
  • Output ordering: Stockham auto-sorting → natural order X_0, X_1, ..., X_{N-1}. Matches FFTW. ✓
  • fft_t simplified: pointer + c_ptr plan → allocatable :: samples. No memory leaks. fft_deinit deallocates. ✓
  • Null check: if (.not. allocated(fft%samples)) replaces c_associated checks. Simpler, correct. ✓

Build System

  • FetchContent guard: if(NOT TARGET fortnum) prevents double-declaration when libneo pulls fortnum transitively. ✓
  • -I${fortnum_SOURCE_DIR}/include: needed for C files to find fortnum.h. CI confirms it works (build passes).
  • GSL::gsl retained: other MEPHIT code still uses GSL (not in this diff). ✓
  • No remaining gsl_/fftw/FFTW references in src/. ✓

Issues Found

Minor (should fix before merge)

  1. cmake/LICENSE.FindFFTW is a stale orphan — license file for the deleted FindFFTW.cmake. Should be removed.

  2. setup/debian.sh still lists libfftw3-dev (line 16) — no longer needed by MEPHIT core. Misleading but not harmful (package still installed in CI).

Informational (pre-existing, not introduced by this PR)

  1. CI does not run behavioral tests.github/workflows/main.yml only runs make (compile). No make test or ctest. No add_test registrations in CMakeLists.txt. mephit_test.f90 is an integration test that doesn't directly exercise the changed numerical paths (FFT, Bessel, QAG, Levin-u). The numerical correctness of this PR's changes is not validated by CI — only compilation is checked. This is a pre-existing gap, not a regression.

  2. gsl_set_error_handler(gsl_errno_msg) removedmephit_run.c:133 no longer installs a custom GSL error handler. If remaining GSL code (outside this diff) triggers an error, it will use GSL's default abort() instead of the old log-and-exit behavior. The old gsl_errno_msg function is deleted from mephit_util.c/.h. This is acceptable for this PR since no code in the diff uses GSL, but the change in error behavior for other GSL paths should be noted.

  3. fortnum_integrate_qag status discardedhyper1F1.c:61,64 doesn't check the return value. The old code also didn't check gsl_integration_qag's return. If integration fails, f_re/f_im may contain partial results. Same behavior as before.

  4. Fortnum pinned to commit 92de6e9 (not a release tag) — libneo also uses untagged main. MEPHIT pins for reproducibility. Acceptable but means the pinned commit could be force-pushed/rebased.

Verification Summary

  • CI: Build and test passes (run 27566580024). Compiles cleanly. No warnings reported.
  • No remaining GSL/FFTW references in src/ (verified with grep -rn).
  • All numerical APIs verified against fortnum source at pinned commit (QAG, Levin-u, Gauss-Legendre, Bessel I_n, FFT).
  • Sign conventions, normalization, output ordering all match the replaced GSL/FFTW calls.

@slopqueue slopqueue Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict: Approve

Summary

PR #32 replaces FFTW and several GSL routines (numerical quadrature, Levin-u series acceleration, Gauss-Legendre fixed quadrature, Bessel I_n, and the GSL error handler) with the new fortnum library. The changes span C, Fortran, and CMake: cmake/FindFFTW.cmake and src/fftw3.f90 are deleted; CMakeLists.txt fetches fortnum via FetchContent (guarded against double-declaration from libneo), links it instead of FFTW, and adds the fortnum include path to C compile flags. The fft_t type in mephit_util.f90 is simplified from raw FFTW C pointers to Fortran allocatables. Clean, well-scoped dependency-reduction PR with no blockers.

Findings

  1. [minor] CMakeLists.txt:62,188 — GSL is now a dead dependency. find_package(GSL REQUIRED) and GSL::gsl in MEPHIT_LIBS remain, but no source file includes GSL headers or calls GSL functions anymore (confirmed by grepping src/). Consider dropping the GSL dependency in a follow-up.

  2. [minor] README.md:14 — FFTW3 is still listed as a prerequisite. The code no longer links or includes FFTW; this documentation is now stale.

  3. [minor] src/hyper1F1.c:296 — kummer_modified_0_accel error message lost diagnostic value. Previously printed w->terms_used (actual terms used by Levin acceleration, which may be < N). Now prints N (total requested terms). Same error condition (err > 1e-16), just a less informative log.

  4. [minor] src/mephit_util.f90:36–43 — fft_t type appears unused (pre-existing dead code). No file in src/ references fft_t, fft_init, fft_apply, or fft_deinit outside the definition itself. Predates this PR; the simplification (allocatables + fft_c2c) is good cleanup but untested in this repo.

Verdict

Approve. FFTW is cleanly removed; fortnum is correctly integrated via FetchContent with a re-declaration guard; the FFT sign convention (fft_c2c(…, -1) = FFTW FFTW_FORWARD) and normalization (manual /N) match the old path; and the Bessel symmetry mapping (I_{-n}=I_n) in mephit_pert.f90 is correct. Remaining items are non-blocking and suitable for follow-up.

@slopqueue slopqueue Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict: Approve

Summary

PR #32 replaces the remaining GSL and FFTW numerical routines (Bessel I_n, adaptive quadrature, Levin-u summation, c2c FFT) with equivalents from the lazily-fetched fortrran/fortnum, and drops the FFTW build dependency. The index/sign/normalization mappings are correct, including the edge case at pol_mode = 0 (using I_{-n}=I_n), and the in-place FFT is properly guarded by copying the samples before transforming. The migration is functionally sound.

Findings

  1. minorCMakeLists.txt:62,188: find_package(GSL REQUIRED) and GSL::gol3 are still in the build even though no source file (C or Fortran) includes or calls any GSL symbol (confirmed by grep). The PR's stated goal is to drop GSL, and FFTW was fully removed (FindFFTW.cmake deleted, ${FFTW_LIBRARIES} removed from libs). Leftover unused link deps should be removed for a clean break. Similarly, README.md:13-14 still lists both GSL and FFTW3 as prerequisites, and setup/debian.sh:6 still installs libfftw3-dev; both should be updated so contributors don't install libraries the code no longer uses.

Verdict

Approve — the numerical replacements are correct and well-motivated; only the build-system/doc cleanup of now-unused GSL/FFTW references is left, which is non-blocking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant