Skip to content

Drop GSL dependency from collision_freqs#286

Closed
krystophny wants to merge 1 commit into
mainfrom
drop-gsl-collision-freqs
Closed

Drop GSL dependency from collision_freqs#286
krystophny wants to merge 1 commit into
mainfrom
drop-gsl-collision-freqs

Conversation

@krystophny

Copy link
Copy Markdown
Member

Closes #285.

collision_freqs linked GPLv3 GSL with PUBLIC visibility for one routine, the
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.

Changes:

  • gsl_sf_gamma replaced by the Fortran 2008 intrinsic gamma().
  • gsl_sf_gamma_inc_P replaced by an in-tree regularized lower incomplete
    gamma, written from scratch: DLMF 8.11.4 series for x < a + 1, DLMF 8.9.2
    continued fraction (modified Lentz) otherwise.
  • find_package(GSL REQUIRED) and GSL::gsl removed from
    src/collisions/CMakeLists.txt; libgsl-dev removed from CI and
    setup/debian.sh; gsl removed from flake.nix.
  • GSL stays as an optional test oracle: with find_package(GSL QUIET) found,
    test_incomplete_gamma_gsl_oracle.x compares the in-tree function against
    gsl_sf_gamma_inc_P * gsl_sf_gamma at 1944 points (a in [0.25, 6], x in
    [1e-8, 100]) with relative tolerance 1e-12. Without GSL the test is skipped
    and nothing links it.
  • test_collision_freqs.x gains a reference-value test for
    lower_incomplete_gamma against 32 mpmath values (40-digit precision),
    relative tolerance 1e-12, so the function is verified on systems without GSL.

The existing calc_perp_coll_freq expected values, produced by the GSL-backed
implementation, pass unchanged.

Verification

Before (main): configure without GSL fails.

$ cmake -S . -B build_nogsl -G Ninja -DCMAKE_DISABLE_FIND_PACKAGE_GSL=TRUE
CMake Error at src/collisions/CMakeLists.txt:4 (find_package):
  find_package for module GSL called with REQUIRED, but
  CMAKE_DISABLE_FIND_PACKAGE_GSL is enabled.  A REQUIRED package cannot be
  disabled.

After (this branch): same configuration succeeds, build and tests pass without
GSL.

$ cmake -S . -B build_nogsl -G Ninja -DCMAKE_DISABLE_FIND_PACKAGE_GSL=TRUE
configure exit: 0
$ ctest -R "collision|incomplete"
1/1 Test #19: test_collision_freqs .............   Passed    0.30 sec
100% tests passed, 0 tests failed out of 1

After, with GSL present (Homebrew GSL 2.8): oracle agrees with the in-tree
implementation.

$ ctest -R "collision|incomplete_gamma|transport" --output-on-failure
1/3 Test #19: test_collision_freqs ...............   Passed    0.28 sec
2/3 Test #20: test_incomplete_gamma_gsl_oracle ...   Passed    0.26 sec
3/3 Test #21: test_transport .....................   Passed    0.24 sec
100% tests passed, 0 tests failed out of 3

GSL appears on no link line except the optional oracle test:

$ rg -n "gsl" build.ninja | grep -v incomplete_gamma_gsl_oracle | grep LINK
(no output)

Full suite: 56/70 pass; the 14 failures (chartmap/map2disc, tilted_coil) fail
identically on unmodified origin/main in this environment (missing optional
map2disc Python module) and are unrelated to this change.

GSL is GPLv3 and was linked PUBLIC, propagating the GPL obligation to
every MIT-licensed downstream consumer. Replace gsl_sf_gamma with the
Fortran 2008 intrinsic gamma() and gsl_sf_gamma_inc_P with an in-tree
regularized lower incomplete gamma: DLMF 8.11.4 series for x < a + 1,
DLMF 8.9.2 continued fraction otherwise.

GSL remains an optional test oracle. When CMake finds it,
test_incomplete_gamma_gsl_oracle.x checks the in-tree function against
GSL at 1944 points (a in [0.25, 6], x in [1e-8, 100]) with relative
tolerance 1e-12. A reference-value test against 32 mpmath values covers
systems without GSL.

Closes #285
@krystophny krystophny requested a review from marjohma June 9, 2026 18:43
@krystophny

Copy link
Copy Markdown
Member Author

@marjohma this is high prio, as it affects licensing of all our codes. As soon as they link to GSL, they must all be distributed under GPL (it is not a catastrophe if we distribute them under MIT, so not an immediate legal problem, but the GPL conditions automatically convert this to GPL per definition if you link GPL software to it, and I prefer to keep MIT).

@krystophny

Copy link
Copy Markdown
Member Author

Superseded by #316, which routes the collision_freqs lower incomplete gamma to the shared fortnum core (gamma_lower) instead of a libneo-internal reimplementation. Same public interface, same GSL drop; consumers depend on fortnum directly. Leaving this open for a human to close.

@krystophny

Copy link
Copy Markdown
Member Author

Superseded by #316, which routes collision_freqs to fortnum gamma_lower. #316 builds 362/362 with test_collision_freqs and test_transport green and identical pre-existing failures to main. Closing in favor of #316.

@krystophny krystophny closed this Jun 14, 2026
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.

`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 #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.
krystophny added a commit that referenced this pull request Jun 15, 2026
Stacked on #286. Adds `THIRD_PARTY.md` and a README license section; no
code
changes.

The repository labels itself MIT in `LICENSE`, `fpm.toml`, and the
pyproject
classifier, but the tree contains and links code under other licenses.
This PR
documents the actual state:

- `src/transport/gen_laguerre_rule.f90` is John Burkardt's LGPL Fortran
90
version of IQPACK (ACM TOMS Algorithm 655), compiled into the
`transport`
  static library. The entry states the LGPL relink obligation for binary
  distributors; replacement with an MIT implementation is filed as #288.
- FFTW3 (GPL-2.0-or-later) is linked into `magfie`: the same MIT/GPL
conflict
  as the GSL link removed in #286. Removal is filed as #287.
- `python/scripts/ninja_syntax.py` (Apache-2.0, Google) and
`cmake/Modules/findFFTW/` (BSD-3-Clause) are build tooling; neither is
in
  the installed wheel.
- The optional GSL test oracle from #286: the test source is MIT
(GPL-compatible); the linked test executable is a combined work under
GPL
terms, but GPL obligations attach on conveyance and the executable is
never
installed or distributed, so none arise and nothing GSL-related reaches
  shipped artifacts.
- Permissive linked libraries (HDF5, NetCDF, OpenBLAS/LAPACK), the GCC
runtimes (GPL with Runtime Library Exception, no copyleft effect), and
the
  separately installed Python dependencies including LGPL f90wrap.

## Verification

Before: no third-party license documentation existed and the README had
no
license section.

```
$ git show drop-gsl-collision-freqs:THIRD_PARTY.md
fatal: path 'THIRD_PARTY.md' exists on disk, but not in 'drop-gsl-collision-freqs'
$ git grep -i -n "license" drop-gsl-collision-freqs -- README.md
(no output)
```

Facts checked against the tree:

```
$ rg -n "GNU LGPL" src/transport/gen_laguerre_rule.f90 | head -1
18:!    This code is distributed under the GNU LGPL license.
$ rg -n "FFTW::" src/magfie/CMakeLists.txt
30:    FFTW::DoubleThreads
31:    FFTW::Double
$ rg -A2 "tool.scikit-build.wheel.packages" pyproject.toml
[tool.scikit-build.wheel.packages]
libneo = "python/libneo"
efit_to_boozer = "python/efit_to_boozer"
```

After: prose passes the writing checker; docs-only diff, build
unaffected.
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.

Drop GPL GSL dependency from collision_freqs (license conflict with MIT)

1 participant