Skip to content

Route collision_freqs incomplete gamma through fortnum#316

Merged
krystophny merged 4 commits into
mainfrom
drop-gsl/fortnum-collision-freqs
Jun 15, 2026
Merged

Route collision_freqs incomplete gamma through fortnum#316
krystophny merged 4 commits into
mainfrom
drop-gsl/fortnum-collision-freqs

Conversation

@krystophny

@krystophny krystophny commented Jun 14, 2026

Copy link
Copy Markdown
Member

Merge order

Merge sequence: #316 -> #317 -> #318.

Each PR is based on main individually, so each diff is cumulative against main and the three share code. Merge them in order. Once a predecessor merges, the next PR's diff shrinks to its own increment.

collision_freqs linked GPLv3 GSL for one routine: the unnormalized lower
incomplete gamma in the Chandrasekhar function. The GPL obligation reached
every MIT-licensed downstream consumer (SIMPLE, NEO-2, NEO-RT, MEPHIT, KAMEL,
rabe), including SIMPLE's published binary wheels.

This routes that single math call to the fortnum numerical core instead of a
libneo-internal replacement. Consumers depend on fortnum directly.

lower_incomplete_gamma returned gsl_sf_gamma_inc_P(a, x) * gsl_sf_gamma(a),
the unnormalized lower incomplete gamma. That product is fortnum
gamma_lower(a, x), a pure function with the same domain (error stop on
a <= 0 or x < 0, per fortnum docs/migration_libneo.md). The public
interface of lower_incomplete_gamma is unchanged, so callers compile without
edits.

Changes:

  • Replace the two bind(c) GSL interfaces and the line-160 product with a
    single use fortnum_special, only: gamma_lower. The now-dead
    iso_c_binding import is removed.
  • Drop find_package(GSL REQUIRED) and the GSL::gsl link from
    src/collisions/CMakeLists.txt; link the fortnum Fortran target instead.
  • Fetch fortnum via FetchContent from git@github.com:lazy-fortran/fortnum.git
    at main, guarded by if(NOT TARGET fortnum) so a repo that also pulls
    fortnum transitively via libneo does not double-declare the target. This
    mirrors how NEO-2 PR add stellarator symmetric bc file block writer #90 wires fortnum.

This was libneo's only GSL site (no FGSL/SLATEC/AMOS/C-GSL elsewhere), so GSL
is fully dropped.

Supersedes #286: that PR dropped GSL with a libneo-internal reimplementation;
this one routes the same call to the shared fortnum core.

Verification

Environment: gfortran, system HDF5 2.1.1, NetCDF, FFTW, OpenBLAS, fortnum
fetched fresh from lazy-fortran/fortnum main (8f4c71c).

Configure (fortnum fetched and built):

$ cmake -S . -B build -G Ninja -DLIBNEO_BUILD_TESTING=ON
...
-- Performing Test FORTNUM_HAVE_HEAP_TRAMPOLINES - Success
...
-- Configuring done (7.5s)
-- Generating done (0.2s)
configure exit: 0

Full build:

$ cmake --build build -j$(nproc)
[362/362] Linking Fortran shared module _efit_to_boozer.cpython-314-x86_64-linux-gnu.so
full build exit: 0

GSL appears on no link line in the generated build:

$ grep -ri "gsl" build/build.ninja | grep -iE "link|gsl::"
(no output)

The migrated routine is exercised by test_collision_freqs
(calc_perp_coll_freq -> psi_of_x -> lower_incomplete_gamma ->
gamma_lower), whose expected values were produced by the prior GSL-backed
implementation. They pass unchanged:

$ ctest -R "test_collision_freqs|test_transport" --output-on-failure
    Start 19: test_collision_freqs
1/2 Test #19: test_collision_freqs .............   Passed    0.07 sec
    Start 20: test_transport
2/2 Test #20: test_transport ...................   Passed    0.06 sec
100% tests passed, 0 tests failed out of 2

Full suite: 57/69 pass. The 12 failures (chartmap / map2disc / vector
conversion / refcoords) come from a missing optional map2disc Python module
(ModuleNotFoundError: No module named 'map2disc') and the chartmap fixtures
that depend on it. They fail identically on unmodified origin/main built in
the same environment:

$ diff <main failed set> <branch failed set>
IDENTICAL FAILURE SETS
branch count: 12  main count: 12

None of the 12 touch collision_freqs, species, transport, or fortnum.

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

collision_freqs linked GPLv3 GSL for one routine: the unnormalized lower
incomplete gamma in the Chandrasekhar function. The GPL obligation reached
every MIT-licensed downstream consumer (SIMPLE, NEO-2, NEO-RT, MEPHIT, KAMEL,
rabe), including SIMPLE's published binary wheels.

lower_incomplete_gamma returned gsl_sf_gamma_inc_P(a, x) * gsl_sf_gamma(a),
the unnormalized lower incomplete gamma. That is fortnum gamma_lower(a, x),
a pure function with the same domain (error stop on a <= 0 or x < 0). The
public interface of lower_incomplete_gamma is unchanged, so callers compile
without edits.

- Replace the two bind(c) GSL interfaces and the line-160 product with a
  single use of fortnum gamma_lower. The now-dead iso_c_binding import goes.
- Drop find_package(GSL REQUIRED) and the GSL::gsl link from
  src/collisions/CMakeLists.txt; link the fortnum Fortran target instead.
- Fetch fortnum via FetchContent from lazy-fortran/fortnum at main, guarded
  by if(NOT TARGET fortnum) so a repo that also pulls fortnum transitively
  via libneo does not double-declare the target.

This was libneo's only GSL site; GSL is now fully dropped.
@krystophny krystophny merged commit 0032708 into main Jun 15, 2026
4 checks passed
@krystophny krystophny deleted the drop-gsl/fortnum-collision-freqs branch June 15, 2026 20:10
krystophny added a commit that referenced this pull request Jun 15, 2026
## Merge order

Merge sequence: #316 -> #317 -> #318.

Each PR is based on `main` individually, so each diff is cumulative
against `main` and the three share code. Merge them in order. Once a
predecessor merges, the next PR's diff shrinks to its own increment.

## Scope

src/magfie/coil_tools.f90 used FFTW3 for the one-dimensional
real-to-complex transforms in `biot_savart_fourier` and
`vector_potential_biot_savart_fourier`. This re-homes the field of the
math-kit PR #290 / FFTW-drop PR #287 by routing those transforms through
fortnum instead of an in-tree math kit. fortnum `fft_r2c` shares FFTW's
unnormalized forward convention and `n/2+1` complex packing, so the
transform output is identical and both public subroutine interfaces are
unchanged. The FFTW `find_package` / `ExternalProject` paths and the
bundled `cmake/Modules/findFFTW` module are removed; fortnum is fetched
via `FetchContent` at tag `a7faa3c`, guarded by `if(NOT TARGET
fortnum)`.

## Dependency removed

FFTW3 (`src/fftw3.F90` binding wrapper, `FFTW::Double` /
`FFTW::DoubleThreads` link, FFTW detection and from-source
ExternalProject).

## Verification

Configure (no FFTW detection runs; fortnum fetched at a7faa3c):

```
-- === Detecting External Dependencies ===
-- Found system HDF5: 2.1.1
--   -> ABI compatible, using system HDF5
--   -> ABI compatible, using system NetCDF
-- === Dependency Detection Complete ===
-- Configuring done (9.8s)
-- Build files have been written to: /tmp/libneo-l1
```

Build: `[570/570] Linking Fortran shared module
_efit_to_boozer.cpython-314-x86_64-linux-gnu.so`

No FFTW symbols remain in the magfie archive:

```
$ nm /tmp/libneo-l1/src/magfie/libmagfie.a | grep -i fftw
$ echo $?   # (no matches)
```

FFT-path tests (Biot-Savart Fourier modes and field validation) pass:

```
1/6 Test #50: test_biotsavart ..................   Passed    0.45 sec
2/6 Test #52: test_biotsavart_field ............   Passed    0.07 sec
3/6 Test #59: test_coil_tools_biot_savart ......   Passed   29.23 sec
4/6 Test #60: tilted_coil_generate_geometry ....   Passed    0.13 sec
5/6 Test #61: tilted_coil_fourier_modes ........   Passed  199.77 sec
6/6 Test #62: tilted_coil_field_validation .....   Passed   12.36 sec

100% tests passed, 0 tests failed out of 6
```

Note: fortnum pin updated to current main (92de6e9) after a fortnum
history rewrite; old shas no longer resolve.
krystophny added a commit that referenced this pull request Jun 15, 2026
…le (#318)

## Merge order

Merge sequence: #316 -> #317 -> #318.

Each PR is based on `main` individually, so each diff is cumulative
against `main` and the three share code. Merge them in order. Once a
predecessor merges, the next PR's diff shrinks to its own increment.

## Scope

src/transport/gen_laguerre_rule.f90 was an LGPL generalized
Gauss-Laguerre quadrature generator (`cgqf` / `r8_huge` / `rule_write`).
This re-homes the field of the math-kit PR #290 /
LGPL-quadrature-removal PR #288 by routing the rule through fortnum
instead of an in-tree math kit. `init_gauss_laguerre_integration` called
the LGPL code only to build the order-32 rule for the weight `x^alpha
exp(-x)` on `[0, inf)` with `alpha = 5/2` (D11) and `7/2` (D12); the
`a=0, b=1` scaling is the identity. fortnum `gauss_gen_laguerre(n,
alpha, x, w)` returns exactly that rule (Golub-Welsch), so the public
interfaces of `init_gauss_laguerre_integration` and `calc_D_one_over_nu`
are unchanged and the D11/D12 reference values reproduce within
tolerance.

Stacked on #317 (FFTW drop); review and merge that first.

## Dependency removed

The LGPL `src/transport/gen_laguerre_rule.f90` quadrature generator (and
the dead `calculate_gauss_laguerre_rule` / `write_to_file` / `debugging`
state that only fed it).

## Verification

Build (stack tip, both aspects applied):

```
[568/568] ...
ninja: no work to do.
```

No LGPL quadrature symbols remain in the transport archive (only the
fortnum reference):

```
$ nm /tmp/libneo-l2/src/transport/libtransport.a | grep -iE 'cgqf|r8_huge|rule_write|gen_laguerre'
                 U __fortnum_quadrature_MOD_gauss_gen_laguerre
```

Transport test (D11e/D11i/D12e/D12i within 5% of references) passes:

```
    Start 20: test_transport
1/1 Test #20: test_transport ...................   Passed    0.11 sec

100% tests passed, 0 tests failed out of 1
```

Note: fortnum pin updated to current main (92de6e9) after a fortnum
history rewrite; old shas no longer resolve.
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