We are more than happy to announce the first release of the Fortran standard library (stdlib). We just published version 0.1.0:
Many thanks go to all the contributors at stdlib participating in the discussions, reviewing patches and sharing code. Almost two years of work are collected in this release since the project started in Dec 2019. (We plan to release in shorter intervals going forward.)
Feedback is very welcome, feel free leave a comment here or open a new issue at the stdlib repository. We are also always looking for new contributors and reviewers.
You can find the full changelog below or in the linked release.
The following modules are available in version 0.1.0:
I suspect you already know this, but I tried building stdlib with Intel oneAPI Classic 2021.3 on Windows (an untested combination as Iām well aware) using cmake, just out of interestā¦
I get a compilation error cascade starting with
2>Compiling with IntelĀ® Fortran Compiler Classic 2021.3.0 [Intel(R) 64]ā¦
2>stdlib_stats_moment.f90
2>C:\Source\stdlib-0.1.0\build\src\stdlib_stats_moment.f90(1): error #8765: Error in opening the compiled ancestor module file. [STDLIB_STATS]
Looks like the submodule/module dependency checking needs fixing.
I note thereās an open issue #368 (Add testing workflow for Intel oneAPI on Windows) on GitHub; I assume this will be covered by that?
On Windows things are always strange and confusing to me, but maybe I can still help. Would you mind sharing which version of CMake you used and whether the build backend was VS or Ninja? If you didnāt specify the generator (-G option) than CMake will default to VS project files, not sure how well those work for tracking dependencies in Fortran projects with submodules.
We are currently testing stdlib mainly in MSYS2 environments using Ninja as build backend (-GNinja).
Yes - congratulations and thanks for all your hard work.
I have grabbed it and have started building and noticed that there are shed loads of warning messages when Iād expect libraries to build cleanly - no doubt this is easy to fix.
More seriously I think there is a structural problem. I picked one file, median, and the source code is over 77k lines long and the .o is over 159MB This is far too big for general use. It looks as if fpm generates all combinations that can be legal. If thatās true I suggest that every one gets written to itās own source file and compiled separately, that way when building from an archive only the routines that are actually used will be pulled in.
Avoiding this sort of bloat is one of the reasons C++ is introducing modules so it would be good not to start behind the curve on this one.
Since templates arenāt part of the language yet we are a bit hampered but would it be possible to use the old cfront method that C++ used to use? On the HP at least it would compile and link the program then deduce the template instantiations it needed from the undefined symbols, write the necessary code then build and link a second time - slow but always interesting to watch!
With gfortran I use the -Wall option to turn on warnings but also use -Wno-maybe-uninitialized -Wno-surprising to turn some off because I found them to generally produce spurious warnings. Many of the the gfortran warnings for stdlib are -Wmaybe-uninitialized . Gfortran warnings do not necessarily indicate a problem with stdlib, but if certain warnings are going to be ignored, maybe they should turned off in the builds.
I think we currently have the warning level for GFortran and Intel Fortran maxed in the CMake build files, which is probably not a good idea since we are using a lot of implicit conversions from integer to real in stdlib among others. The amount of warnings in stdlib needs some serious trimming down, starting by reducing the signal-to-noise level in the compiler options to create the warnings.
Right. I am not worried about the warnings (which can be fixed one way or another ā however indeed things should build cleanly, so we do have to fix it), but this is indeed a real problem.
It seems generating all array ranks and types and kinds like that indeed introduces too much bloat which is unsustainable in practice.
Itās an interesting idea to deduce what instantiation have to happen from undefined symbols. But it seems we need to be able to reliably obtain those on all platforms, and then hook it in a feedback loop. And every Fortran compiler has different name mangling, so we would also have to take that into account. We could probably teach fpm to do that. However in modern Fortran things would not even compile if the given (overloaded) subroutine is not available in the module. So I donāt even know if this is technically doable. We could infer from the compiler error messages what needs to be built.
A more technically sound idea is to implement generics in LFortran, and then LFortran can āinstantiateā only what is needed for a given project and write it down as Fortran source file, then one can use any Fortran compiler to compile. And we can integrate the LFortranās library with fpm, so that fpm does this automatically under the hood.
Still, I am not happy with the solution. I would like to just distribute Fortran sources of the stdlib library that just work, no hacks or code generation required. But I donāt know how.
Yes, it indeed makes sense to start this. Iām familiar with packaging for conda, spack, brew, Arch and MSYS2 and will probably submit stdlib for those at some point. But I wonāt touch a deb any time soon (why does this stuff need to be so complicated?).
Submitting a conda-forge recipe seems like the next step, since I anyway have to package a lot of Fortran projects there in the next time. Maybe even with a short tutorial in case people are interested in how its done.
I already posted a guide for packaging with MSYS2 here:
I donāt understand why stdlib has to be so complex. In windows itās so difficult to setup.
Why canāt it be some simple modules which people can download and use ? Why such huge arrangement for generics - fypp. Use only Fortran and with good documentation things should be easy. Since in Fortran we donāt have so many types such as int, float, long, doubleā¦ I donāt see the such dire need for generics.
This is how NAG and IMSL libraries are written. Just because c++ has generics, we need not run after them.
So in total 5 different integer kinds, and 4 real kinds with this compiler.
For a fully-fledged library the routines in stdlib need to specialize for each kind, some also for type (e.g. sort), and some also for rank (e.g. mean).
Hence, the need for a preprocessor and the large number of procedures and huge object files.
If the procedures from stdlib were ever to become standardized by the WG5 Fortran Standard committee, then the vendors/compiler implementers could delegate the code generation step to the compiler with the major advantage that only procedures which are actually called are generated.
I donāt expect this to happen any time soon. The more realistic options is what @certik suggested in post #9.
If anyone is in charge of negotiating a large deal for a HPC facility, they can also force such things into the contract with the compiler vendors. I guess we still need some time to reach this point.
Indeed, 5+4=9 options just for real/integer. And then multiply this by the ranks that you want to support, so at least 3, more likely 6 and up to 15, for algorithms that cannot be expressed as elemental. Say we only want to support up to 6, to be conservative, so thatās 54 total subroutines to generate. A lot of times only real makes sense, so that would be 24. Still that is a lot.
Letās worry about the āany rankā later. First letās design a good extension for the kinds. What would be the most natural way to do it? Letās worry about only the real type first. Should we do something like real(*) (or full: real(kind=*))? It seems we want the variable for the kind. So perhaps this:
subroutine f(x, y)
integer, parameter :: wp = kind(x)
real(wp), intent(in) :: x
real(wp), intent(out) :: y
y = sin(x)**2 + erf(x)
end subroutine
Then itās up to the compiler how it will actually get it done but it would do something equivalent to generating all 4 kinds (for example it can just generate it ahead of time, or only on demand for what is actually used, etc.):
interface
module procedure f4, f8
end interface
...
subroutine f4(x, y)
integer, parameter :: wp = 4
real(wp), intent(in) :: x
real(wp), intent(out) :: y
y = sin(x)**2 + erf(x)
end subroutine
subroutine f8(x, y)
integer, parameter :: wp = 8
real(wp), intent(in) :: x
real(wp), intent(out) :: y
y = sin(x)**2 + erf(x)
end subroutine
What would be the best design of āany kindā generics?
One further thing to consider. If you want Fortranās generics to be useful in higher level code, you need to be able to deal with structured matrices. Julia makes it possible to have types like BandedMatrix{Nhi, Nlo} where depending on N you can get a bidiagonal, tridiagonal, or any other type of banded structure. This is really useful for things like writing efficient solvers for partial differential equations.
It would be really cool if user defined matrices could somehow reuse the natural Fortran array syntax. I believe C++ will now be able to do exactly that: