diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cf8980c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,34 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Override syntax highlighting +*.fypp linguist-language=fortran + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.h text +*.f90 text +*.F90 text +*.md text +*.txt text +*.sh text +*.cu text + +# Denote all files that are truly binary and should not be modified. +*.mod binary +*.o binary +*.a binary +*.so binary +*.tar binary +*.gz binary +*.tgz binary + +# Prevent dev-ops files from making it into the release archives +.gitattributes export-ignore +.gitignore export-ignore +codecov.yml export-ignore +.github export-ignore + +# Perform substitutions when `git export`ing these files +.VERSION export-subst diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..779d334 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,93 @@ +name: CI + +on: [push, pull_request] + +env: + CMAKE_BUILD_PARALLEL_LEVEL: "2" # 2 cores on each GHA VM, enable parallel builds + CTEST_OUTPUT_ON_FAILURE: "ON" # This way we don't need a flag to ctest + CTEST_PARALLEL_LEVEL: "2" + CTEST_TIME_TIMEOUT: "5" # some failures hang forever + HOMEBREW_NO_ANALYTICS: "ON" # Make Homebrew installation a little quicker + HOMEBREW_NO_AUTO_UPDATE: "ON" + HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" + HOMEBREW_NO_GITHUB_API: "ON" + HOMEBREW_NO_INSTALL_CLEANUP: "ON" + +jobs: + Build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-13] + toolchain: + - {compiler: gcc, version: 10} + - {compiler: gcc, version: 11} + - {compiler: gcc, version: 12} + - {compiler: gcc, version: 13} + - {compiler: intel, version: '2024.1'} + build: [cmake] + include: + - os: ubuntu-22.04 + build: cmake + toolchain: {compiler: intel-classic, version: '2021.10'} + - os: ubuntu-latest + build: cmake-inline + toolchain: {compiler: gcc, version: 10} + exclude: + - os: macos-13 + toolchain: {compiler: intel, version: '2024.1'} + - os: macos-13 + toolchain: {compiler: gcc, version: 13} + env: + BUILD_DIR: ${{ matrix.build == 'cmake' && 'build' || '.' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python 3.x + uses: actions/setup-python@v5 # Use pip to install latest CMake, & FORD/Jin2For, etc. + with: + python-version: 3.x + + - name: Install fypp + run: pip install --upgrade fypp ninja + + - name: Setup Fortran compiler + uses: fortran-lang/setup-fortran@v1.6.2 + id: setup-fortran + with: + compiler: ${{ matrix.toolchain.compiler }} + version: ${{ matrix.toolchain.version }} + + # Build and test with built-in BLAS and LAPACK + - name: Configure with CMake + if: ${{ contains(matrix.build, 'cmake') }} + run: >- + cmake -Wdev -G Ninja + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_MAXIMUM_RANK:String=4 + -DCMAKE_INSTALL_PREFIX=$PWD/_dist + -S . -B ${{ env.BUILD_DIR }} + + - name: Build and compile + if: ${{ contains(matrix.build, 'cmake') }} + run: cmake --build ${{ env.BUILD_DIR }} --parallel + + - name: catch build fail + if: ${{ failure() && contains(matrix.build, 'cmake') }} + run: cmake --build ${{ env.BUILD_DIR }} --verbose --parallel 1 + + - name: test + if: ${{ contains(matrix.build, 'cmake') }} + run: >- + ctest + --test-dir ${{ env.BUILD_DIR }} + --parallel + --output-on-failure + --no-tests=error + + - name: Install project + if: ${{ contains(matrix.build, 'cmake') }} + run: cmake --install ${{ env.BUILD_DIR }} diff --git a/.github/workflows/CI_windows.yml b/.github/workflows/CI_windows.yml new file mode 100644 index 0000000..ead22a7 --- /dev/null +++ b/.github/workflows/CI_windows.yml @@ -0,0 +1,68 @@ +name: CI_windows + +on: [push, pull_request] + +env: + CTEST_TIME_TIMEOUT: "5" # some failures hang forever + CMAKE_GENERATOR: Ninja + +jobs: + msys2-build: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: [ + { msystem: MINGW64, arch: x86_64 } + ] + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v2 + + - name: Setup MinGW native environment + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + update: false + install: >- + git + mingw-w64-${{ matrix.arch }}-gcc + mingw-w64-${{ matrix.arch }}-gcc-fortran + mingw-w64-${{ matrix.arch }}-python + mingw-w64-${{ matrix.arch }}-python-fypp + mingw-w64-${{ matrix.arch }}-cmake + mingw-w64-${{ matrix.arch }}-ninja + + - run: >- + PATH=$PATH:/mingw64/bin/ cmake + -Wdev + -B build + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_Fortran_FLAGS_DEBUG="-Wall -Wextra -Wimplicit-interface -fPIC -g -fcheck=all -fbacktrace" + -DCMAKE_MAXIMUM_RANK:String=4 + -DCMAKE_INSTALL_PREFIX=$PWD/_dist + env: + FC: gfortran + CC: gcc + CXX: g++ + + - name: CMake build + run: PATH=$PATH:/mingw64/bin/ cmake --build build --parallel + + - name: catch build fail + run: PATH=$PATH:/mingw64/bin/ cmake --build build --verbose --parallel 1 + if: failure() + + - name: CTest + run: PATH=$PATH:/mingw64/bin/ ctest --test-dir build --output-on-failure --parallel -V -LE quadruple_precision + + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: WindowsCMakeTestlog + path: build/Testing/Temporary/LastTest.log + + - name: Install project + run: PATH=$PATH:/mingw64/bin/ cmake --install build diff --git a/.github/workflows/PR-review.yml b/.github/workflows/PR-review.yml new file mode 100644 index 0000000..c16d0ee --- /dev/null +++ b/.github/workflows/PR-review.yml @@ -0,0 +1,17 @@ +name: PR-Review +on: [pull_request] +jobs: + misspell: + name: review-dog / misspell + runs-on: ubuntu-latest + steps: + - name: Check out code. + uses: actions/checkout@v2 + - name: misspell + uses: reviewdog/action-misspell@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + locale: "US" + reporter: github-pr-review + level: warning + ignore: colour diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index 12f654b..0000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CMake - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - BUILD_TYPE: Release - -jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest] - toolchain: - - {compiler: gcc, version: 11} - - {compiler: intel-classic, version: '2021.9'} - include: - - os: ubuntu-latest - toolchain: {compiler: intel, version: '2023.2'} - - steps: - - uses: awvwgk/setup-fortran@v1 - id: setup-fortran - with: - compiler: ${{ matrix.toolchain.compiler }} - version: ${{ matrix.toolchain.version }} - - - run: ${{ env.FC }} --version - env: - FC: ${{ steps.setup-fortran.outputs.fc }} - CC: ${{ steps.setup-fortran.outputs.cc }} - - - uses: actions/checkout@v3 - - - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_Fortran_COMPILER=${{ env.FC }} -DBUILD_TESTING=TRUE - - - name: Build with CMake - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - - name: Test with CMake - working-directory: ${{github.workspace}}/build - run: ctest -C ${{env.BUILD_TYPE}} \ No newline at end of file diff --git a/.github/workflows/fpm-deployment.yml b/.github/workflows/fpm-deployment.yml new file mode 100644 index 0000000..2e7dc50 --- /dev/null +++ b/.github/workflows/fpm-deployment.yml @@ -0,0 +1,90 @@ +name: fpm-deployment + +on: [push, pull_request] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + toolchain: {compiler: gcc, version: 14} + + steps: + - name: Checkout code + uses: actions/checkout@v2.3.1 + + - name: Set up Python 3.x + uses: actions/setup-python@v1 + with: + python-version: 3.x + + - name: Install requirements + run: pip install --upgrade -r config/requirements.txt + + - uses: fortran-lang/setup-fortran@main + id: setup-fortran + with: + compiler: ${{ matrix.toolchain.compiler }} + version: ${{ matrix.toolchain.version }} + + - name: Setup Fortran Package Manager + uses: fortran-lang/setup-fpm@v5 + with: + fpm-version: 'v0.10.0' + + - name: Prepare for code coverage + if: contains( matrix.os, 'ubuntu') + run: | + sudo apt-get install lcov + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install -y gcc-14 gfortran-14 + sudo update-alternatives \ + --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 \ + --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-14 \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-14 + + - run: | # Just for deployment: create fftpack-fpm folder + python config/fypp_deployment.py --deploy_fftpack_fpm + + - run: | # Just for deployment: create fftpack-fpm-ilp64 folder + python config/fypp_deployment.py --deploy_fftpack_fpm --with_ilp64 + + - run: | # Use fpm gnu ci to check xdp and qp + python config/fypp_deployment.py --with_xdp --with_qp + fpm test --profile release --flag '-DWITH_XDP -DWITH_QP -coverage' + + - name: Create coverage report + run: | + mkdir -p ${{ env.COV_DIR }} + mv ./build/gfortran_*/*/* ${{ env.COV_DIR }} + lcov --capture --initial --base-directory . --directory ${{ env.COV_DIR }} --output-file ${{ env.COV_DIR }}/coverage.base + lcov --capture --base-directory . --directory ${{ env.COV_DIR }} --output-file ${{ env.COV_DIR }}/coverage.capture + lcov --add-tracefile ${{ env.COV_DIR }}/coverage.base --add-tracefile ${{ env.COV_DIR }}/coverage.capture --output-file ${{ env.COV_DIR }}/coverage.info + env: + COV_DIR: build/coverage + + - name: Upload coverage report + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: build/coverage/coverage.info + + # Update and deploy the f90 files generated by github-ci to the `fftpack-fpm` branch. + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@4.1.5 + if: github.event_name != 'pull_request' + with: + BRANCH: fftpack-fpm + FOLDER: fftpack-fpm + + # Update and deploy the f90 files generated by github-ci to the `fftpack-fpm-ilp64` branch. + - name: Deploy with 64-bit integer support 🚀 + uses: JamesIves/github-pages-deploy-action@4.1.5 + if: github.event_name != 'pull_request' + with: + BRANCH: fftpack-fpm-ilp64 + FOLDER: fftpack-fpm-ilp64 diff --git a/.github/workflows/fpm.yml b/.github/workflows/fpm.yml deleted file mode 100644 index 6e31100..0000000 --- a/.github/workflows/fpm.yml +++ /dev/null @@ -1,138 +0,0 @@ -name: fpm - -on: [push, pull_request] - -jobs: - gcc-build: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-13] - gcc_v: [13] # Version of GFortran we want to use. - include: - - os: ubuntu-latest - os-arch: linux-x86_64 - - - os: macos-13 - os-arch: macos-x86_64 - - env: - FC: gfortran - GCC_V: ${{ matrix.gcc_v }} - - steps: - - name: Checkout code - uses: actions/checkout@v1 - - - name: Install GFortran Linux - if: contains(matrix.os, 'ubuntu') - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \ - --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \ - --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} - - # Backport gfortran shared libraries to version 9 folder. This is necessary because the macOS release of fpm - # 0.10.0 used for bootstrapping has these paths hardcoded in the executable. - # See https://github.com/fortran-lang/fpm/pull/1061 - - name: MacOS patch libgfortran - if: contains(matrix.os, 'macos') - run: | - ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran - which gfortran-${GCC_V} - which gfortran - mkdir /usr/local/opt/gcc@10 - mkdir /usr/local/opt/gcc@10/lib - mkdir /usr/local/opt/gcc@10/lib/gcc - mkdir /usr/local/opt/gcc@10/lib/gcc/10 - mkdir /usr/local/lib/gcc/10 - ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib - ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib - ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - - - name: Install fpm - uses: fortran-lang/setup-fpm@v5 - with: - fpm-version: 'v0.9.0' - - - name: Build fftpack - run: | - gfortran --version - fpm build - - - name: Run tests - run: | - gfortran --version - fpm test - - msys2-build: - runs-on: windows-latest - defaults: - run: - shell: msys2 {0} - - steps: - - uses: actions/checkout@v2 - - uses: msys2/setup-msys2@v2 - with: - msystem: MINGW64 - update: true - path-type: inherit - install: | - mingw-w64-x86_64-gcc-fortran - mingw-w64-x86_64-fpm - - - name: fpm build - run: | - gfortran --version - fpm --version - fpm build - - - name: fpm test - run: | - fpm test - - intel-build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - - env: - FPM_FC: ifx - FC: ifx - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Add Intel repository (Linux) - run: | - wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB - sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB - rm GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB - echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list - sudo apt-get update - - - name: Install Intel oneAPI compiler (Linux) - run: | - sudo apt-get install intel-oneapi-compiler-fortran - - - name: Setup Intel oneAPI environment - run: | - source /opt/intel/oneapi/setvars.sh - printenv >> $GITHUB_ENV - - - name: Install fpm - uses: fortran-lang/setup-fpm@v3 - with: - fpm-version: 'v0.8.2' - - - name: fpm build - run: | - ifx --version - fpm --version - fpm build --profile debug --flag "-warn nointerfaces" - - - name: fpm test - run: | - fpm test --profile debug --flag "-warn nointerfaces" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 12334fd..a9ea74b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,53 @@ -build* +build/ +**/temp/ +# FORD generated documentation +# !WARNING! This folder gets deleted and overwritten +API-doc/ + +# Prerequisites +*.d -# Compiled objects +# Compiled Object files +*.slo +*.lo *.o -*.a +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries *.so -*.x +*.dylib +*.dll + +# Fortran module files *.mod *.smod -# API-doc -API-doc/ +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Build directory for out-of-tree builds +/build +/fftpack-fpm + +# Emacs backup files +*~ -# Meson subprojects -subprojects/test-drive \ No newline at end of file +# Files generated by tests +*.bin +*log*.txt +*test*.txt +scratch.txt +*.dat +*.stream diff --git a/CMakeLists.txt b/CMakeLists.txt index 91c7e6b..c586978 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,44 +1,90 @@ -# Master CMAKE Build Script -cmake_minimum_required(VERSION 3.24) -project( - fftpack - LANGUAGES Fortran - VERSION 1.0.0 +cmake_minimum_required(VERSION 3.14.0) + +# Include overwrites before setting up the project +set(CMAKE_USER_MAKE_RULES_OVERRIDE ${CMAKE_CURRENT_SOURCE_DIR}/config/DefaultFlags.cmake) + +project(fortran_fftpack + LANGUAGES Fortran + DESCRIPTION "A modern Fortran implementation of fftpack" ) -# Get helper macros and functions -include("${PROJECT_SOURCE_DIR}/cmake/helper.cmake") +# Read version from file +file(STRINGS "${PROJECT_SOURCE_DIR}/VERSION" PROJECT_VERSION) +string(REPLACE "." ";" VERSION_LIST ${PROJECT_VERSION}) +list(GET VERSION_LIST 0 PROJECT_VERSION_MAJOR) +list(GET VERSION_LIST 1 PROJECT_VERSION_MINOR) +list(GET VERSION_LIST 2 PROJECT_VERSION_PATCH) +unset(VERSION_LIST) + +include(CTest) -# Confgiure everything -add_subdirectory(configure) +# Follow GNU conventions for installation directories +include(GNUInstallDirs) -# Source -add_subdirectory(src) -add_fortran_library( - ${PROJECT_NAME} - ${PROJECT_INCLUDE_DIR} - ${CMAKE_INSTALL_INCLUDEDIR} - ${PROJECT_VERSION} - ${PROJECT_VERSION_MAJOR} - ${FFTPACK_SOURCES} +include(${PROJECT_SOURCE_DIR}/cmake/fftpack.cmake) + +# --- CMake specific configuration and package data export +add_subdirectory(config) + +# --- compiler selection +if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 9.0) + message(FATAL_ERROR "GCC Version 9 or newer required") +endif() + +# --- silence gfortran-15 argument-mismatch warnings +if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU + AND CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0 + AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 16.0) + add_compile_options("$<$:-Wno-external-argument-mismatch>") +endif() + +# --- compiler feature checks +include(CheckFortranSourceCompiles) +include(CheckFortranSourceRuns) +check_fortran_source_runs("i=0; error stop i; end" f18errorstop) +check_fortran_source_compiles("real, allocatable :: array(:, :, :, :, :, :, :, :, :, :); end" f03rank SRC_EXT f90) +check_fortran_source_runs("use, intrinsic :: iso_fortran_env, only : real128; real(real128) :: x; x = x+1; end" f03real128) + +if(NOT DEFINED CMAKE_MAXIMUM_RANK) + set(CMAKE_MAXIMUM_RANK 4 CACHE STRING "Maximum array rank for generated procedures") +endif() + +# --- find preprocessor +find_program(FYPP fypp) +if(NOT FYPP) + message(FATAL_ERROR "Preprocessor fypp not found! Please install fypp following the instructions in https://fypp.readthedocs.io/en/stable/fypp.html#installing") +endif() + +# Custom preprocessor flags +if(DEFINED CMAKE_MAXIMUM_RANK) + set(fyppFlags "-DMAXRANK=${CMAKE_MAXIMUM_RANK}") +elseif(f03rank) + set(fyppFlags) +else() + set(fyppFlags "-DVERSION90") +endif() + +list( + APPEND fyppFlags + "-DWITH_CBOOL=$" + "-DWITH_QP=$" + "-DWITH_XDP=$" + "-DWITH_ILP64=$" + "-DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}" + "-DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR}" + "-DPROJECT_VERSION_PATCH=${PROJECT_VERSION_PATCH}" + "-I${PROJECT_SOURCE_DIR}/include" ) -# Installation -add_subdirectory(install) +add_subdirectory(src) -# Testing -option(BUILD_TESTING "Build tests") -include(CTest) -message(STATUS "Build FFTPACK tests: ${BUILD_TESTING}") -if (BUILD_TESTING) +if(BUILD_TESTING) enable_testing() - add_subdirectory(dependencies) add_subdirectory(test) + add_subdirectory(example) endif() -# Examples -option(BUILD_FFTPACK_EXAMPLES "Build FFTPACK examples") -message(STATUS "Build FFTPACK examples: ${BUILD_FFTPACK_EXAMPLES}") -if (BUILD_FFTPACK_EXAMPLES) - add_subdirectory(example) -endif() \ No newline at end of file +install(EXPORT ${PROJECT_NAME}-targets + NAMESPACE ${PROJECT_NAME}:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" +) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..fcdb2e1 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +4.0.0 diff --git a/cmake/fftpack.cmake b/cmake/fftpack.cmake new file mode 100644 index 0000000..6f4960d --- /dev/null +++ b/cmake/fftpack.cmake @@ -0,0 +1,48 @@ +# Preprocesses a list of files with given preprocessor and preprocessor options +# +# Args: +# preproc [in]: Preprocessor program +# preprocopts [in]: Preprocessor options +# srcext [in]: File extension of the source files +# trgext [in]: File extension of the target files +# srcfiles [in]: List of the source files +# trgfiles [out]: Contains the list of the preprocessed files on exit +# +function(preprocess preproc preprocopts srcext trgext srcfiles trgfiles) + + set(_trgfiles) + foreach(srcfile IN LISTS srcfiles) + string(REGEX REPLACE "\\.${srcext}$" ".${trgext}" trgfile ${srcfile}) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${trgfile} + COMMAND ${preproc} ${preprocopts} ${CMAKE_CURRENT_SOURCE_DIR}/${srcfile} ${CMAKE_CURRENT_BINARY_DIR}/${trgfile} + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${srcfile}) + list(APPEND _trgfiles ${CMAKE_CURRENT_BINARY_DIR}/${trgfile}) + endforeach() + set(${trgfiles} ${_trgfiles} PARENT_SCOPE) + +endfunction() + + + +# Preprocesses fortran files with fypp. +# +# It assumes that source files have the ".fypp" extension. Target files will be +# created with the extension ".f90". The FYPP variable must contain the path to +# the fypp-preprocessor. +# +# Args: +# fyppopts [in]: Options to pass to fypp. +# fyppfiles [in]: Files to be processed by fypp +# f90files [out]: List of created f90 files on exit +# +function (fypp_f90 fyppopts fyppfiles f90files) + preprocess("${FYPP}" "${fyppopts}" "fypp" "f90" "${fyppfiles}" _f90files) + set(${f90files} ${_f90files} PARENT_SCOPE) +endfunction() + +# For fortran sources that contain C preprocessor flags: create ".F90" files +function (fypp_f90pp fyppopts fyppfiles F90files) + preprocess("${FYPP}" "${fyppopts}" "fypp" "F90" "${fyppfiles}" _F90files) + set(${F90files} ${_F90files} PARENT_SCOPE) +endfunction() diff --git a/cmake/helper.cmake b/cmake/helper.cmake deleted file mode 100644 index 7a560ce..0000000 --- a/cmake/helper.cmake +++ /dev/null @@ -1,75 +0,0 @@ -# helper.cmake -# -# A collection of macros and functions making life with CMake and Fortran a -# bit simpler. - -# Use to include and export headers -function(include_headers lib dir install_dir) - target_include_directories( - ${lib} - INTERFACE - $ - $ - ) -endfunction() - -# Use instead of add_library. -function(add_fortran_library lib_name mod_dir include_install_dir version major) - add_library(${lib_name} ${ARGN}) - set_target_properties( - ${lib_name} - PROPERTIES - POSITION_INDEPENDENT_CODE TRUE - OUTPUT_NAME ${lib_name} - VERSION ${version} - SOVERSION ${major} - Fortran_MODULE_DIRECTORY ${include_install_dir} - ) - target_include_directories( - ${lib_name} - PUBLIC - $ - $ - ) -endfunction() - -# Installs the library -function(install_library lib_name lib_install_dir bin_install_dir mod_dir install_dir) - install( - TARGETS ${lib_name} - EXPORT ${lib_name}Targets - RUNTIME DESTINATION ${bin_install_dir} - LIBRARY DESTINATION ${lib_install_dir} - ARCHIVE DESTINATION ${lib_install_dir} - INCLUDES DESTINATION ${install_dir}/include - ) - install( - DIRECTORY ${mod_dir} - DESTINATION ${install_dir} - ) -endfunction() - -# Install the documentation files -function(install_documentation doc_dir install_dir) - install( - DIRECTORY ${doc_dir} - DESTINATION ${install_dir} - ) -endfunction() - -# Links the supplied library -function(link_library targ lib include_dir) - target_link_libraries(${targ} ${lib}) - target_include_directories(${targ} PUBLIC $) -endfunction() - -# ------------------------------------------------------------------------------ -# Helpful Macros -macro(print_all_variables) - message(STATUS "---------- CURRENTLY DEFINED VARIABLES -----------") - get_cmake_property(varNames VARIABLES) - foreach(varName ${varNames}) - message(STATUS ${varName} = ${${varName}}) - endforeach() - message(STATUS "---------- END ----------") -endmacro() \ No newline at end of file diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt new file mode 100644 index 0000000..d81373a --- /dev/null +++ b/config/CMakeLists.txt @@ -0,0 +1,85 @@ +# SPDX-Identifier: MIT + +if(NOT DEFINED CMAKE_INSTALL_MODULEDIR) + set( + CMAKE_INSTALL_MODULEDIR + "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/${CMAKE_Fortran_COMPILER_ID}-${CMAKE_Fortran_COMPILER_VERSION}" + CACHE + STRING + "Directory in prefix to install generated module files" + ) +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) + +# Check for available features +# Note: users can overwrite the automatic check by setting the value at configure time +include(CheckFortranSourceRuns) +if (NOT DEFINED WITH_CBOOL) + check_fortran_source_runs( + "use, intrinsic :: iso_c_binding, only: c_bool; integer, parameter :: lk = kind(.true.) + if (c_bool == lk) stop 1 + end" + WITH_CBOOL + ) + set(WITH_CBOOL ${WITH_CBOOL} PARENT_SCOPE) +endif() +if (NOT DEFINED WITH_QP) + check_fortran_source_runs( + "if (selected_real_kind(33) == -1) stop 1; end" + WITH_QP + ) + set(WITH_QP ${WITH_QP} PARENT_SCOPE) +endif() +if (NOT DEFINED WITH_XDP) + check_fortran_source_runs( + "if (any(selected_real_kind(18) == [-1, selected_real_kind(33)])) stop 1; end" + WITH_XDP + ) + set(WITH_XDP ${WITH_XDP} PARENT_SCOPE) +endif() +# Check if WITH_ILP64 is defined; if not, set it to FALSE +if (NOT DEFINED WITH_ILP64) + set(WITH_ILP64 FALSE) + set(WITH_ILP64 ${WITH_ILP64} PARENT_SCOPE) +endif() + +# Export a pkg-config file +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/template.pc" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + @ONLY +) +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" +) + +# Export CMake package file +include(CMakePackageConfigHelpers) +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/template.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" +) +if(BUILD_SHARED_LIBS OR PROJECT_VERSION_MAJOR EQUAL 0) + # Due to the uncertain ABI compatibility of Fortran shared libraries + # limit compatibility for dynamic linking to same minor version. + set(COMPATIBILITY SameMinorVersion) +else() + # Require API compatibility via semantic versioning for static linking. + set(COMPATIBILITY SameMajorVersion) +endif() +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" + VERSION "${PROJECT_VERSION}" + COMPATIBILITY ${COMPATIBILITY} +) +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" +) diff --git a/config/DefaultFlags.cmake b/config/DefaultFlags.cmake new file mode 100644 index 0000000..eafe7a4 --- /dev/null +++ b/config/DefaultFlags.cmake @@ -0,0 +1,50 @@ +if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + set( + CMAKE_Fortran_FLAGS_INIT + "-fimplicit-none" + "-ffree-line-length-132" + ) + set( + CMAKE_Fortran_FLAGS_RELEASE_INIT + ) + set( + CMAKE_Fortran_FLAGS_DEBUG_INIT + "-Wall" + "-Wextra" + "-Wimplicit-procedure" + "-std=f2018" + ) +elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") + set( + CMAKE_Fortran_FLAGS_INIT + ) + set( + CMAKE_Fortran_FLAGS_RELEASE_INIT + ) + if(WIN32) + set( + CMAKE_Fortran_FLAGS_DEBUG_INIT + "/stand:f18" + "/warn:declarations,general,usage,interfaces,unused" + ) + else() + set( + CMAKE_Fortran_FLAGS_DEBUG_INIT + "-stand f18" + "-warn declarations,general,usage,interfaces,unused" + ) + endif() +else() + set( + CMAKE_Fortran_FLAGS_INIT + ) + set( + CMAKE_Fortran_FLAGS_RELEASE_INIT + ) + set( + CMAKE_Fortran_FLAGS_DEBUG_INIT + ) +endif() +string(REPLACE ";" " " CMAKE_Fortran_FLAGS_INIT "${CMAKE_Fortran_FLAGS_INIT}") +string(REPLACE ";" " " CMAKE_Fortran_FLAGS_RELEASE_INIT "${CMAKE_Fortran_FLAGS_RELEASE_INIT}") +string(REPLACE ";" " " CMAKE_Fortran_FLAGS_DEBUG_INIT "${CMAKE_Fortran_FLAGS_DEBUG_INIT}") diff --git a/config/cmake/Findtest-drive.cmake b/config/cmake/Findtest-drive.cmake new file mode 100644 index 0000000..8337347 --- /dev/null +++ b/config/cmake/Findtest-drive.cmake @@ -0,0 +1,178 @@ +# SPDX-Identifier: MIT + +#[[.rst: +Find test-drive +--------------- + +Makes the test-drive project available. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported target, if found: + +``test-drive::test-drive`` + The test-drive library + + +Result Variables +^^^^^^^^^^^^^^^^ + +This module will define the following variables: + +``TEST_DRIVE_FOUND`` + True if the test-drive library is available + +``TEST_DRIVE_SOURCE_DIR`` + Path to the source directory of the test-drive project, + only set if the project is included as source. + +``TEST_DRIVE_BINARY_DIR`` + Path to the binary directory of the test-drive project, + only set if the project is included as source. + +Cache variables +^^^^^^^^^^^^^^^ + +The following cache variables may be set to influence the library detection: + +``TEST_DRIVE_FIND_METHOD`` + Methods to find or make the project available. Available methods are + - ``cmake``: Try to find via CMake config file + - ``pkgconf``: Try to find via pkg-config file + - ``subproject``: Use source in subprojects directory + - ``fetch``: Fetch the source from upstream + +``TEST_DRIVE_DIR`` + Used for searching the CMake config file + +``TEST_DRIVE_SUBPROJECT`` + Directory to find the test-drive subproject, relative to the project root + +#]] + +set(_lib "test-drive") +set(_pkg "TEST_DRIVE") +set(_url "https://github.com/fortran-lang/test-drive") + +if(NOT DEFINED "${_pkg}_FIND_METHOD") + if(DEFINED "${PROJECT_NAME}-dependency-method") + set("${_pkg}_FIND_METHOD" "${${PROJECT_NAME}-dependency-method}") + else() + set("${_pkg}_FIND_METHOD" "cmake" "pkgconf" "subproject" "fetch") + endif() + set("_${_pkg}_FIND_METHOD") +endif() + +foreach(method ${${_pkg}_FIND_METHOD}) + if(TARGET "${_lib}::${_lib}") + break() + endif() + + if("${method}" STREQUAL "cmake") + message(STATUS "${_lib}: Find installed package") + if(DEFINED "${_pkg}_DIR") + set("_${_pkg}_DIR") + set("${_lib}_DIR" "${_pkg}_DIR") + endif() + find_package("${_lib}" CONFIG QUIET) + if("${_lib}_FOUND") + message(STATUS "${_lib}: Found installed package") + break() + endif() + endif() + + if("${method}" STREQUAL "pkgconf") + find_package(PkgConfig QUIET) + pkg_check_modules("${_pkg}" QUIET "${_lib}") + if("${_pkg}_FOUND") + message(STATUS "Found ${_lib} via pkg-config") + + add_library("${_lib}::${_lib}" INTERFACE IMPORTED) + target_link_libraries( + "${_lib}::${_lib}" + INTERFACE + "${${_pkg}_LINK_LIBRARIES}" + ) + target_include_directories( + "${_lib}::${_lib}" + INTERFACE + "${${_pkg}_INCLUDE_DIRS}" + ) + + break() + endif() + endif() + + if("${method}" STREQUAL "subproject") + if(NOT DEFINED "${_pkg}_SUBPROJECT") + set("_${_pkg}_SUBPROJECT") + set("${_pkg}_SUBPROJECT" "subprojects/${_lib}") + endif() + set("${_pkg}_SOURCE_DIR" "${PROJECT_SOURCE_DIR}/${${_pkg}_SUBPROJECT}") + set("${_pkg}_BINARY_DIR" "${PROJECT_BINARY_DIR}/${${_pkg}_SUBPROJECT}") + if(EXISTS "${${_pkg}_SOURCE_DIR}/CMakeLists.txt") + message(STATUS "Include ${_lib} from ${${_pkg}_SUBPROJECT}") + add_subdirectory( + "${${_pkg}_SOURCE_DIR}" + "${${_pkg}_BINARY_DIR}" + ) + + add_library("${_lib}::${_lib}" INTERFACE IMPORTED) + target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") + + # We need the module directory in the subproject before we finish the configure stage + if(NOT EXISTS "${${_pkg}_BINARY_DIR}/include") + file(MAKE_DIRECTORY "${${_pkg}_BINARY_DIR}/include") + endif() + + break() + endif() + endif() + + if("${method}" STREQUAL "fetch") + message(STATUS "Retrieving ${_lib} from ${_url}") + include(FetchContent) + FetchContent_Declare( + "${_lib}" + GIT_REPOSITORY "${_url}" + GIT_TAG "v0.4.0" + ) + FetchContent_MakeAvailable("${_lib}") + + add_library("${_lib}::${_lib}" INTERFACE IMPORTED) + target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") + + # We need the module directory in the subproject before we finish the configure stage + FetchContent_GetProperties("${_lib}" SOURCE_DIR "${_pkg}_SOURCE_DIR") + FetchContent_GetProperties("${_lib}" BINARY_DIR "${_pkg}_BINARY_DIR") + if(NOT EXISTS "${${_pkg}_BINARY_DIR}/include") + file(MAKE_DIRECTORY "${${_pkg}_BINARY_DIR}/include") + endif() + + break() + endif() + +endforeach() + +if(TARGET "${_lib}::${_lib}") + set("${_pkg}_FOUND" TRUE) +else() + set("${_pkg}_FOUND" FALSE) +endif() + +if(DEFINED "_${_pkg}_SUBPROJECT") + unset("${_pkg}_SUBPROJECT") + unset("_${_pkg}_SUBPROJECT") +endif() +if(DEFINED "_${_pkg}_DIR") + unset("${_lib}_DIR") + unset("_${_pkg}_DIR") +endif() +if(DEFINED "_${_pkg}_FIND_METHOD") + unset("${_pkg}_FIND_METHOD") + unset("_${_pkg}_FIND_METHOD") +endif() +unset(_lib) +unset(_pkg) +unset(_url) diff --git a/config/fypp_deployment.py b/config/fypp_deployment.py new file mode 100644 index 0000000..94b55bc --- /dev/null +++ b/config/fypp_deployment.py @@ -0,0 +1,177 @@ +import os +import fypp +import argparse +from joblib import Parallel, delayed + +C_PREPROCESSED = ( + # "stdlib_linalg_constants" , + # "stlib_linalg_blas" , + # "stdlib_linalg_lapack", + # "test_blas_lapack" +) + + +def pre_process_fypp(args): + """use fypp to preprocess all source files. + + Processed files will be dumped at /temp/ or + + Parameters + ---------- + args : + CLI arguments. + """ + kwd = [] + kwd.append("-DMAXRANK="+str(args.maxrank)) + kwd.append("-DPROJECT_VERSION_MAJOR="+str(args.vmajor)) + kwd.append("-DPROJECT_VERSION_MINOR="+str(args.vminor)) + kwd.append("-DPROJECT_VERSION_PATCH="+str(args.vpatch)) + if args.with_qp: + kwd.append("-DWITH_QP=True") + if args.with_xdp: + kwd.append("-DWITH_XDP=True") + if args.with_ilp64: + kwd.append("-DWITH_ILP64=True") + + optparser = fypp.get_option_parser() + options, leftover = optparser.parse_args(args=kwd) + options.includes = ['include'] + if args.lnumbering: + options.line_numbering = True + tool = fypp.Fypp(options) + + # Check destination folder for preprocessing. if not 'fftpack-fpm', it is assumed to be the root folder. + if not os.path.exists('src'+os.sep+'temp'): + os.makedirs('src'+os.sep+'temp') + if not os.path.exists('test'+os.sep+'temp'): + os.makedirs('test'+os.sep+'temp') + + # Define the folders to search for *.fypp files + folders = ['src', 'test'] + # Process all folders + fypp_files = [os.path.join(root, file) for folder in folders + for root, _, files in os.walk(folder) + for file in files if file.endswith(".fypp")] + + def process_f(file): + source_file = file + root = os.path.dirname(file) + if not os.path.exists(root+os.sep+'temp'): + os.makedirs(root+os.sep+'temp') + basename = os.path.splitext(os.path.basename(source_file))[0] + sfx = 'f90' if basename not in C_PREPROCESSED else 'F90' + target_file = root+os.sep+'temp' + os.sep + basename + '.' + sfx + tool.process_file(source_file, target_file) + + Parallel(n_jobs=args.njob)(delayed(process_f)(f) for f in fypp_files) + + return + + +def deploy_fftpack_fpm(with_ilp64): + """create the fftpack-fpm folder for backwards compatibility (to be deprecated) + """ + import shutil + prune = ( + "test_hash_functions.f90", + "f18estop.f90", + ) + + if with_ilp64: + base_folder = 'fftpack-fpm-ilp64' + else: + base_folder = 'fftpack-fpm' + + if not os.path.exists(base_folder+os.sep+'src'): + os.makedirs(base_folder+os.sep+'src') + if not os.path.exists(base_folder+os.sep+'test'): + os.makedirs(base_folder+os.sep+'test') + if not os.path.exists(base_folder+os.sep+'example'): + os.makedirs(base_folder+os.sep+'example') + + def recursive_copy(folder): + for root, _, files in os.walk(folder): + for file in files: + if file not in prune: + if file.endswith((".f90", ".F90", ".dat", ".npy", ".c", ".h")): + shutil.copy2(os.path.join(root, file), + base_folder+os.sep+folder+os.sep+file) + recursive_copy('src') + recursive_copy('test') + recursive_copy('example') + for file in ['.gitignore', 'fpm.toml', 'LICENSE', 'VERSION']: + shutil.copy2(file, base_folder+os.sep+file) + return + + +def fpm_build(args, unknown): + import subprocess + # ========================================== + # check compilers + FPM_FC = os.environ['FPM_FC'] if "FPM_FC" in os.environ else "gfortran" + FPM_CC = os.environ['FPM_CC'] if "FPM_CC" in os.environ else "gcc" + FPM_CXX = os.environ['FPM_CXX'] if "FPM_CXX" in os.environ else "gcc" + # ========================================== + # Filter out flags + preprocessor = {'gfortran': '-cpp ', 'ifort': '-fpp ', 'ifx': '-fpp '} + flags = preprocessor[FPM_FC] + for idx, arg in enumerate(unknown): + if arg.startswith("--flag"): + flags = flags + unknown[idx+1] + # ========================================== + # build with fpm + subprocess.run("fpm build" + + " --compiler "+FPM_FC + + " --c-compiler "+FPM_CC + + " --cxx-compiler "+FPM_CXX + + " --flag \"{}\"".format(flags), shell=True, check=True) + return + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='Preprocess fftpack source files.') + # fypp arguments + parser.add_argument("--vmajor", type=int, default=0, + help="Project Version Major") + parser.add_argument("--vminor", type=int, default=5, + help="Project Version Minor") + parser.add_argument("--vpatch", type=int, default=0, + help="Project Version Patch") + + parser.add_argument("--njob", type=int, default=4, + help="Number of parallel jobs for preprocessing") + parser.add_argument("--maxrank", type=int, default=4, + help="Set the maximum allowed rank for arrays") + parser.add_argument("--with_qp", action='store_true', + help="Include WITH_QP in the command") + parser.add_argument("--with_xdp", action='store_true', + help="Include WITH_XDP in the command") + parser.add_argument("--with_ilp64", action='store_true', + help="Include WITH_ILP64 to build 64-bit integer BLAS/LAPACK") + parser.add_argument("--lnumbering", action='store_true', + help="Add line numbering in preprocessed files") + parser.add_argument("--deploy_fftpack_fpm", action='store_true', + help="create the fftpack-fpm folder") + # external libraries arguments + parser.add_argument("--build", action='store_true', + help="Build the project") + + args, unknown = parser.parse_known_args() + # ========================================== + # read current manifest + with open('VERSION', 'r') as file: + version = file.read().split(".") + vmajor, vminor, vpatch = [int(value) for value in version] + args.vmajor = max(vmajor, args.vmajor) + args.vminor = max(vminor, args.vminor) + args.vpatch = max(vpatch, args.vpatch) + # ========================================== + # pre process the meta programming fypp files + pre_process_fypp(args) + if args.deploy_fftpack_fpm: + deploy_fftpack_fpm(args.with_ilp64) + # ========================================== + # build using fpm + if args.build: + fpm_build(args, unknown) diff --git a/config/requirements.txt b/config/requirements.txt new file mode 100644 index 0000000..74e6082 --- /dev/null +++ b/config/requirements.txt @@ -0,0 +1,3 @@ +fypp +argparse +joblib \ No newline at end of file diff --git a/config/template.cmake b/config/template.cmake new file mode 100644 index 0000000..576b596 --- /dev/null +++ b/config/template.cmake @@ -0,0 +1,10 @@ +@PACKAGE_INIT@ + +set("@PROJECT_NAME@_WITH_CBOOL" @WITH_CBOOL@) +set("@PROJECT_NAME@_WITH_QP" @WITH_QP@) +set("@PROJECT_NAME@_WITH_XDP" @WITH_XDP@) +set("@PROJECT_NAME@_WITH_ILP64" @WITH_ILP64@) + +if(NOT TARGET "@PROJECT_NAME@::@PROJECT_NAME@") + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") +endif() diff --git a/config/template.pc b/config/template.pc new file mode 100644 index 0000000..eecdda9 --- /dev/null +++ b/config/template.pc @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ +moduledir=${prefix}/@CMAKE_INSTALL_MODULEDIR@ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -l@PROJECT_NAME@ +Cflags: -I${includedir} -I${moduledir} diff --git a/configure/CMakeLists.txt b/configure/CMakeLists.txt deleted file mode 100644 index f114e3c..0000000 --- a/configure/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Get the macros and functions we'll need -include("${PROJECT_SOURCE_DIR}/cmake/helper.cmake") - -# Set a default build type if none was specified -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") -endif() - -# By default, static library -option(BUILD_SHARED_LIBS "Build shared libraries" OFF) - -# Export all symbols on Windows when building libraries -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) - -# Utilize the GNU installation structure -include(GNUInstallDirs) - -# Locate the local include directory -set(PROJECT_INCLUDE_DIR ${PROJECT_BINARY_DIR}/include) -set(PROJECT_INCLUDE_DIR ${PROJECT_INCLUDE_DIR} PARENT_SCOPE) \ No newline at end of file diff --git a/configure/install-mod.py b/configure/install-mod.py deleted file mode 100644 index 0e65ce2..0000000 --- a/configure/install-mod.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 - -from os import environ, listdir, makedirs -from os.path import join, isdir, exists -from sys import argv -from shutil import copy - -build_dir = environ["MESON_BUILD_ROOT"] -if "MESON_INSTALL_DESTDIR_PREFIX" in environ: - install_dir = environ["MESON_INSTALL_DESTDIR_PREFIX"] -else: - install_dir = environ["MESON_INSTALL_PREFIX"] - -include_dir = argv[1] if len(argv) > 1 else "include" -module_dir = join(install_dir, include_dir) - -modules = [] -for d in listdir(build_dir): - bd = join(build_dir, d) - if isdir(bd): - for f in listdir(bd): - if f.endswith(".mod"): - modules.append(join(bd, f)) - -if not exists(module_dir): - makedirs(module_dir) - -for mod in modules: - print("Installing", mod, "to", module_dir) - copy(mod, module_dir) diff --git a/configure/meson.build b/configure/meson.build deleted file mode 100644 index 1192d66..0000000 --- a/configure/meson.build +++ /dev/null @@ -1,24 +0,0 @@ -os = host_machine.system() -fc = meson.get_compiler('fortran') -cc = fc -fc_id = fc.get_id() - -if fc_id == 'gcc' - add_project_arguments( - '-ffree-line-length-none', - '-fbacktrace', - language: 'fortran', - ) -elif fc_id == 'intel' - add_project_arguments( - '-traceback', - language: 'fortran', - ) -elif fc_id == 'pgi' or fc_id == 'nvidia_hpc' - add_project_arguments( - '-Mbackslash', - '-Mallocatable=03', - '-traceback', - language: 'fortran', - ) -endif diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt deleted file mode 100644 index 7922e30..0000000 --- a/dependencies/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(test-drive) -set(test-drive_LIBRARY ${test-drive_LIBRARY} PARENT_SCOPE) -set(test-drive_INCLUDE_DIR ${test-drive_INCLUDE_DIR} PARENT_SCOPE) \ No newline at end of file diff --git a/dependencies/test-drive/CMakeLists.txt b/dependencies/test-drive/CMakeLists.txt deleted file mode 100644 index 6950ff9..0000000 --- a/dependencies/test-drive/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -include(FetchContent) - -# If found, use the installed version; else, import the library -if (${test-drive_FOUND}) - # Inform the user of what's going on - message(STATUS "TEST-DRIVE (${test-drive_VERSION}) library found.") - - # Get the mod file location - get_target_property(test-drive_INCLUDE_DIR test-drive INTERFACE_INCLUDE_DIRECTORIES) -else() - # Inform the user of what's going on - message(STATUS "TEST-DRIVE library not found. Downloading appropriate repository.") - - # Fetch the proper content - FetchContent_Declare( - test-drive - GIT_TAG "origin/main" - GIT_REPOSITORY "https://github.com/fortran-lang/test-drive" - OVERRIDE_FIND_PACKAGE - ) - - FetchContent_MakeAvailable(test-drive) - set(test-drive_INCLUDE_DIR ${test-drive_BINARY_DIR}/include) -endif() - -# Make a parent-scope variable for the library -set(test-drive_LIBRARY test-drive) -set(test-drive_LIBRARY ${test-drive_LIBRARY} PARENT_SCOPE) - -# Make a parent-scope variable locating the include directory for test-drive -set(test-drive_INCLUDE_DIR ${test-drive_INCLUDE_DIR} PARENT_SCOPE) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index ddd4b43..9f8bcef 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,17 +1,13 @@ -add_executable(bench1 bench01_zfft.f90) -target_link_libraries(bench1 fftpack) - -add_executable(bench2 bench02_zfft.f90) -target_link_libraries(bench2 fftpack) - -add_executable(bench3 bench03_dfft.f90) -target_link_libraries(bench3 fftpack) - -add_executable(rfft_example) -target_link_libraries(rfft_example fftpack) - -add_executable(complex_transforms) -target_link_libraries(complex_transforms fftpack) - -add_executable(real_transforms) -target_link_libraries(real_transforms fftpack) +macro(ADD_EXAMPLE name) + add_executable(example_${name} example_${name}.f90) + target_link_libraries(example_${name} "${PROJECT_NAME}") + add_test(NAME ${name} + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endmacro(ADD_EXAMPLE) + +ADD_EXAMPLE(bench01_zfft) +ADD_EXAMPLE(bench02_zfft) +ADD_EXAMPLE(bench03_dfft) +ADD_EXAMPLE(complex_transforms) +ADD_EXAMPLE(real_transforms) diff --git a/example/bench01_zfft.f90 b/example/example_bench01_zfft.f90 similarity index 100% rename from example/bench01_zfft.f90 rename to example/example_bench01_zfft.f90 diff --git a/example/bench02_zfft.f90 b/example/example_bench02_zfft.f90 similarity index 100% rename from example/bench02_zfft.f90 rename to example/example_bench02_zfft.f90 diff --git a/example/bench03_dfft.f90 b/example/example_bench03_dfft.f90 similarity index 100% rename from example/bench03_dfft.f90 rename to example/example_bench03_dfft.f90 diff --git a/example/complex_transforms.f90 b/example/example_complex_transforms.f90 similarity index 100% rename from example/complex_transforms.f90 rename to example/example_complex_transforms.f90 diff --git a/example/real_transforms.f90 b/example/example_real_transforms.f90 similarity index 100% rename from example/real_transforms.f90 rename to example/example_real_transforms.f90 diff --git a/fpm.toml b/fpm.toml index 9d11836..f7e25d6 100644 --- a/fpm.toml +++ b/fpm.toml @@ -20,15 +20,15 @@ auto-tests = true auto-examples = true [dev-dependencies] -test-drive = { git = "https://github.com/fortran-lang/test-drive", tag = "v0.4.0" } +test-drive = { git = "https://github.com/fortran-lang/test-drive"} -# Original test -[[test]] -name = "tstfft" -source-dir = "test" -main = "tstfft.f" - -[[test]] -name = "test_fftpack" -source-dir = "test" -main = "test_fftpack.f90" +# # Original test +# [[test]] +# name = "tstfft" +# source-dir = "test" +# main = "tstfft.f" +# +# [[test]] +# name = "test_fftpack" +# source-dir = "test" +# main = "test_fftpack.f90" diff --git a/include/common.fypp b/include/common.fypp new file mode 100644 index 0000000..de0a7b9 --- /dev/null +++ b/include/common.fypp @@ -0,0 +1,466 @@ +#:mute + +#! Project version number +#:set PROJECT_VERSION = "{}.{}.{}".format(PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH) + +#! Support for C_BOOL logical +#:if not defined("WITH_CBOOL") +#:set WITH_CBOOL = False +#:endif + +#! Support for quadruple precision floating point numbers +#:if not defined("WITH_QP") +#:set WITH_QP = False +#:endif + +#! Support for extended double precision floating point numbers +#:if not defined("WITH_XDP") +#:set WITH_XDP = False +#:endif + +#! Support for linear algebra with 64-bit integer sizes +#:if not defined("WITH_ILP64") +#:set WITH_ILP64 = False +#:endif + +#! Real kinds to be considered during templating +#:set REAL_KINDS = ["sp", "dp"] +#:if WITH_XDP +#:set REAL_KINDS = REAL_KINDS + ["xdp"] +#:endif +#:if WITH_QP +#:set REAL_KINDS = REAL_KINDS + ["qp"] +#:endif + +#! BLAS/LAPACK initials for each real kind +#:set REAL_INIT = ["s", "d"] +#:if WITH_XDP +#:set REAL_INIT = REAL_INIT + ["x"] +#:endif +#:if WITH_QP +#:set REAL_INIT = REAL_INIT + ["q"] +#:endif + +#! Real types to be considered during templating +#:set REAL_TYPES = ["real({})".format(k) for k in REAL_KINDS] +#:set REAL_SUFFIX = REAL_KINDS + +#! Collected (kind, type) tuples for real types +#:set REAL_KINDS_TYPES = list(zip(REAL_KINDS, REAL_TYPES, REAL_INIT)) + +#! Complex kinds to be considered during templating +#:set CMPLX_KINDS = ["sp", "dp"] +#:if WITH_XDP +#:set CMPLX_KINDS = CMPLX_KINDS + ["xdp"] +#:endif +#:if WITH_QP +#:set CMPLX_KINDS = CMPLX_KINDS + ["qp"] +#:endif + +#! BLAS/LAPACK initials for each complex kind +#:set CMPLX_INIT = ["c", "z"] +#:if WITH_XDP +#:set CMPLX_INIT = CMPLX_INIT + ["y"] +#:endif +#:if WITH_QP +#:set CMPLX_INIT = CMPLX_INIT + ["w"] +#:endif + +#! BLAS/LAPACK complex->real kind initial conversion +#! Converts a BLAS/LAPACK complex kind initial to a real kind initial +#! +#! Args: +#! ci (character): Complex kind initial in ["c","z","y","w"] +#! +#! Returns: +#! Real kind initial in ["s","d","x","q"] or an empty string on invalid input +#! +#:def c2ri(cmplx) +$:"s" if cmplx=="c" else "d" if cmplx=="z" else "x" if cmplx=="y" else "q" if cmplx=="w" else "ERROR" +#:enddef + +#! BLAS/LAPACK/Linear Algebra Integer Kinds +#:set LINALG_INT_KINDS = ["ilp"] +#:set LINALG_INT_SUFFIX = [""] +#:if WITH_ILP64 +#:set LINALG_INT_KINDS = LINALG_INT_KINDS+["ilp64"] +#:set LINALG_INT_SUFFIX = LINALG_INT_SUFFIX+["_I64"] +#:endif +#:set LINALG_INT_TYPES = ["integer({})".format(k) for k in LINALG_INT_KINDS] +#:set LINALG_INT_KINDS_TYPES = list(zip(LINALG_INT_KINDS, LINALG_INT_TYPES, LINALG_INT_SUFFIX)) + +#! Complex types to be considered during templating +#:set CMPLX_TYPES = ["complex({})".format(k) for k in CMPLX_KINDS] +#:set CMPLX_SUFFIX = ["c{}".format(k) for k in CMPLX_KINDS] + +#! Collected (kind, type, initial) tuples for complex types +#:set CMPLX_KINDS_TYPES = list(zip(CMPLX_KINDS, CMPLX_TYPES, CMPLX_INIT)) + +#! Integer kinds to be considered during templating +#:set INT_KINDS = ["int8", "int16", "int32", "int64"] + +#! Integer types to be considered during templating +#:set INT_TYPES = ["integer({})".format(k) for k in INT_KINDS] + +#! Collected (kind, type) tuples for integer types +#:set INT_KINDS_TYPES = list(zip(INT_KINDS, INT_TYPES)) + +#! Logical kinds to be considered during templating +#:set LOG_KINDS = ["lk"] +#:if WITH_CBOOL +#:set LOG_KINDS = LOG_KINDS + ["c_bool"] +#:endif + +#! Logical types to be considered during templating +#:set LOG_TYPES = ["logical({})".format(k) for k in LOG_KINDS] + +#! Collected (kind, type) tuples for logical types +#:set LOG_KINDS_TYPES = list(zip(LOG_KINDS, LOG_TYPES)) + +#! Derived type string_type +#:set STRING_KINDS = ["string_type"] + +#! String types to be considered during templating +#:set STRING_TYPES = ["type({})".format(k) for k in STRING_KINDS] + +#! Collected (kind, type) tuples for string derived types +#:set STRING_KINDS_TYPES = list(zip(STRING_KINDS, STRING_TYPES)) + +#! Derived type bitsets +#:set BITSET_KINDS = ["bitset_64", "bitset_large"] + +#! Bitset types to be considered during templating +#:set BITSET_TYPES = ["type({})".format(k) for k in BITSET_KINDS] + +#! Sparse types to be considered during templating +#:set SPARSE_KINDS = ["COO", "CSR", "CSC", "ELL"] + +#! Whether Fortran 90 compatible code should be generated +#:set VERSION90 = defined('VERSION90') + +#! Ranks to be generated when templates are created +#:if not defined('MAXRANK') + #:if VERSION90 + #:set MAXRANK = 7 + #:else + #:set MAXRANK = 15 + #:endif +#:endif + + +#! Generates an array rank suffix. +#! +#! Args: +#! rank (int): Rank of the variable +#! +#! Returns: +#! Array rank suffix string (e.g. (:,:) if rank = 2) +#! +#:def ranksuffix(rank) +#{if rank > 0}#(${":" + ",:" * (rank - 1)}$)#{endif}# +#:enddef + +#! Generates an empty array rank suffix. +#! +#! Args: +#! rank (int): Rank of the variable +#! +#! Returns: +#! Empty array rank suffix string (e.g. (0,0) if rank = 2) +#! +#:def emptyranksuffix(rank) +#{if rank > 0}#(${"0" + ",0" * (rank - 1)}$)#{endif}# +#:enddef + +#! Generates an array rank suffix with a fixed integer size for all dimensions. +#! +#! Args: +#! rank (int): Rank of the variable +#! size (int): Size along each dimension +#! +#! Returns: +#! Array rank suffix string +#! E.g., +#! fixedranksuffix(3,4) +#! -> (4,4,4) +#! +#:def fixedranksuffix(rank,size) +#{if rank > 0}#(${str(size) + (","+str(size)) * (rank - 1)}$)#{endif}# +#:enddef + +#! Joins stripped lines with given character string +#! +#! Args: +#! txt (str): Text to process +#! joinstr (str): String to use as connector +#! prefix (str): String to add as prefix before the joined text +#! suffix (str): String to add as suffix after the joined text +#! +#! Returns: +#! Lines stripped and joined with the given string. +#! +#:def join_lines(txt, joinstr, prefix="", suffix="") +${prefix + joinstr.join([line.strip() for line in txt.split("\n")]) + suffix}$ +#:enddef + + +#! Brace enclosed, comma separated Fortran expressions for a reduced shape. +#! +#! Rank of the original variable will be reduced by one. The routine generates +#! for each dimension a Fortan expression using merge(), which calculates the +#! size of the array for that dimension. +#! +#! Args: +#! varname (str): Name of the variable to be used as origin +#! origrank (int): Rank of the original variable +#! idim (int): Index of the reduced dimension +#! +#! Returns: +#! Shape expression enclosed in braces, so that it can be used as suffix to +#! define array shapes in declarations. +#! +#:def reduced_shape(varname, origrank, idim) + #:assert origrank > 0 + #:if origrank > 1 + #:call join_lines(joinstr=", ", prefix="(", suffix=")") + #:for i in range(1, origrank) + merge(size(${varname}$, ${i}$), size(${varname}$, ${i + 1}$), mask=${i}$<${idim}$) + #:endfor + #:endcall + #:endif +#:enddef + + +#! Generates a routine name from a generic name, rank, type and kind +#! +#! Args: +#! gname (str): Generic name +#! rank (integer): Rank if exist +#! type (str): Type of the input +#! kind (str): kind of inputs variable +#! suffix (str): other identifier (could be used for output type/kind) +#! +#! Returns: +#! A string with a new name +#! +#:def rname(gname, rank, type, kind, suffix='') + $:"{0}_{1}_{2}{3}_{2}{3}".format(gname, rank, type[0], kind) if suffix == '' else "{0}_{1}_{2}{3}_{4}".format(gname, rank, type[0], kind, suffix) +#:enddef + + +#! Generates an array rank suffix for subarrays reducing the dimension +#! +#! Args: +#! rank (int): Rank of the original variable +#! selectors (array): Dimension and name of the variable(s) +#! +#! Returns: +#! Array rank suffix string enclosed in braces +#! +#! E.g., +#! select_subarray(5 , [(4, 'i'), (5, 'j')]) +#! -> (:, :, :, i, j) +#! +#:def select_subarray(rank, selectors) + #:assert rank > 0 + #:set seldict = dict(selectors) + #:call join_lines(joinstr=", ", prefix="(", suffix=")") + #:for i in range(1, rank + 1) + $:seldict.get(i, ":") + #:endfor + #:endcall +#:enddef + +#! +#! Generates an array rank suffix for subarrays along a dimension +#! +#! Args: +#! varname (str): Name of the variable to be used as origin +#! rank (int): Rank of the original variable +#! dim (int): Dimension of the variable +#! +#! Returns: +#! Array rank suffix string enclosed in braces +#! +#! E.g., +#! select_subvector('j', 5, 2) +#! -> (j1, :, j3, j4, j5) +#! +#! Used, e.g., in +#! stdlib_stats_median.fypp +#! +#:def select_subvector(varname, rank, idim) + #:assert rank > 0 + #:call join_lines(joinstr=", ", prefix="(", suffix=")") + #:for i in range(1, idim) + ${varname}$${i}$ + #:endfor + : + #:for i in range(idim + 1, rank + 1) + ${varname}$${i}$ + #:endfor + #:endcall +#:enddef + +#! +#! Generates an array rank suffix for arrays +#! +#! Args: +#! varname (str): Name of the variable to be used as origin +#! rank (int): Rank of the original array variable +#! idim (int): Dimension of the variable dropped +#! +#! Returns: +#! Array rank suffix string enclosed in braces +#! +#! E.g., +#! reduce_subvector('j', 5, 2) +#! -> (j1, j3, j4, j5) +#! +#! Used, e.g., in +#! stdlib_stats_median.fypp +#! +#:def reduce_subvector(varname, rank, idim) + #:assert rank > 0 + #:if rank > 1 + #:call join_lines(joinstr=", ", prefix="(", suffix=")") + #:for i in range(1, idim) + ${varname}$${i}$ + #:endfor + #:for i in range(idim + 1, rank + 1) + ${varname}$${i}$ + #:endfor + #:endcall + #:endif +#:enddef + +#! +#! Generates a list of loop variables +#! +#! Args: +#! varname(str): Name of the variable to be used as prefix +#! n (int): Number of loop variables to be created +#! offset (int): Optional index offset +#! +#! Returns: +#! Variable definition string +#! +#! E.g., +#! loop_variables('j', 5) +#! -> "j1, j2, j3, j4, j5 +#! +#:def loop_variables(varname, n, offset=0) + #:assert n > 0 + #:call join_lines(joinstr=", ") + #:for i in range(1, n + 1) + ${varname}$${i+offset}$ + #:endfor + #:endcall +#:enddef + +#! +#! Generates a list of loop variables from an array +#! +#! Args: +#! varname(str): Name of the array variable to be used as prefix +#! n (int): Number of loop variables to be created +#! offset (int): Optional index offset +#! +#! Returns: +#! Variable definition string +#! +#! E.g., +#! loop_array_variables('j', 5) +#! -> "j(1), j(2), j(3), j(4), j(5) +#! +#! loop_array_variables('j', 5, 2) +#! -> "j(3), j(4), j(5), j(6), j(7) +#! +#:def loop_array_variables(varname, n, offset=0) + #:assert n > 0 + #:call join_lines(joinstr=", ") + #:for i in range(1, n + 1) + ${varname}$(${i+offset}$) + #:endfor + #:endcall +#:enddef + + +#! Generates an array shape specifier from an N-D array size +#! +#! Args: +#! name (str): Name of the original variable +#! rank (int): Rank of the original variable +#! offset(int): optional offset of the dimension loop (default = 0) +#! +#! Returns: +#! Array rank suffix string enclosed in braces +#! +#! E.g., +#! shape_from_array_size('mat', 5)}$ +#! -> (size(mat,1),size(mat,2),size(mat,3),size(mat,4),size(mat,5)) +#! shape_from_array_size('mat', 5, 2)}$ +#! -> (size(mat,3),size(mat,4),size(mat,5),size(mat,6),size(mat,7)) +#! +#:def shape_from_array_size(name, rank, offset=0) + #:assert rank > 0 + #:call join_lines(joinstr=", ", prefix="(", suffix=")") + #:for i in range(1, rank + 1) + size(${name}$,${i+offset}$) + #:endfor + #:endcall +#:enddef + +#! Generates an array shape specifier from an N-D array of sizes +#! +#! Args: +#! name (str): Name of the original variable +#! rank (int): Rank of the original variable +#! offset(int): optional offset of the dimension loop (default = 0) +#! +#! Returns: +#! Array rank suffix string enclosed in braces +#! +#! E.g., +#! shape_from_array_data('mat', 5)}$ +#! -> (1:mat(1),1:mat(2),1:mat(3),1:mat(4),1:mat(5)) +#! shape_from_array_data('mat', 5, 2)}$ +#! -> (1:mat(3),1:mat(4),1:mat(5),1:mat(6),1:mat(7)) +#! +#:def shape_from_array_data(name, rank, offset=0) + #:assert rank > 0 + #:call join_lines(joinstr=", ", prefix="(", suffix=")") + #:for i in range(1, rank + 1) + 1:${name}$(${i+offset}$) + #:endfor + #:endcall +#:enddef + +#! +#! Start a sequence of loop with indexed variables over an N-D array +#! +#! Args: +#! varname (str): Name of the variable to be used as prefix +#! matname (str): Name of the variable to be used as array +#! n (int): Number of nested loops to be created (1=innermost; n=outermost) +#! dim_offset (int): Optional dimension offset (1st loop is over dimension 1+dim_offset) +#! intent (str): Optional indentation. Default: 8 spaces +#! +#! +#:def loop_variables_start(varname, matname, n, dim_offset=0, indent=" "*8) + #:assert n > 0 + #:for i in range(1, n + 1) +${indent}$do ${varname}$${n+1+dim_offset-i}$ = lbound(${matname}$, ${n+1+dim_offset-i}$), ubound(${matname}$, ${n+1+dim_offset-i}$) + #:endfor +#:enddef + +#:def loop_variables_end(n, indent=" "*8) +#:assert n > 0 + #:call join_lines(joinstr="; ",prefix=indent) + #:for i in range(1, n + 1) + enddo + #:endfor + #:endcall +#:enddef + +#:endmute diff --git a/install/CMakeLists.txt b/install/CMakeLists.txt deleted file mode 100644 index 9298887..0000000 --- a/install/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -# Get the macros and functions we'll need -include("${PROJECT_SOURCE_DIR}/cmake/helper.cmake") -include(CMakePackageConfigHelpers) - -# Install the library and necessary include files -install_library( - ${PROJECT_NAME} - ${CMAKE_INSTALL_LIBDIR} - ${CMAKE_INSTALL_BINDIR} - ${PROJECT_INCLUDE_DIR} - ${CMAKE_INSTALL_PREFIX} -) - -# Define the version file -write_basic_package_version_file( - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion -) - -export( - EXPORT ${PROJECT_NAME}Targets - FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Targets.cmake -) - -# Define the configuration file -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake - COPYONLY -) - -install( - EXPORT ${PROJECT_NAME}Targets - FILE ${PROJECT_NAME}Targets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) -install( - FILES - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/template.pc - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc - @ONLY -) -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig -) \ No newline at end of file diff --git a/install/fftpackConfig.cmake.in b/install/fftpackConfig.cmake.in deleted file mode 100644 index d31b66c..0000000 --- a/install/fftpackConfig.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -if (NOT TARGET fftpack) - include("${CMAKE_CURRENT_LIST_DIR}/fftpackTargets.cmake") -endif() \ No newline at end of file diff --git a/install/template.pc b/install/template.pc deleted file mode 100644 index 1d40466..0000000 --- a/install/template.pc +++ /dev/null @@ -1,9 +0,0 @@ -prefix = @CMAKE_INSTALL_PREFIX@ -libdir = ${prefix}/@CMAKE_INSTALL_FULL_LIBDIR@ -includedir = ${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ - -Name: @PROJECT_NAME@ -Description: FFTPACK is a package of fortran subprograms for the fast fourier transform of periodic and other symmetric sequences. -Version: @PROJECT_VERSION@ -Libs: -L${libdir} -l@PROJECT_NAME@ -Cflags: -I${includedir} \ No newline at end of file diff --git a/meson.build b/meson.build deleted file mode 100644 index 0510396..0000000 --- a/meson.build +++ /dev/null @@ -1,57 +0,0 @@ -project( - 'fftpack', - 'fortran', - version: '4.0.0', - meson_version: '>=0.57', - default_options: [ - 'buildtype=debugoptimized', - 'default_library=both', - 'c_std=c11', - ], -) - -install = not (meson.is_subproject() and get_option('default_library') == 'static') - -# Collect source of the project -srcs = [] -subdir('src') - -# General configuration information -inc_dirs = [] -lib_deps = [] -subdir('configure') - -# Library target -fftpack_lib = library( - meson.project_name(), - sources: srcs, - version: meson.project_version(), - dependencies: lib_deps, - include_directories: inc_dirs, - install: install, -) - -# Export dependency for other projects and test suite -fftpack_dep = declare_dependency( - link_with: fftpack_lib, - dependencies: lib_deps, - variables: {'includedir': meson.current_source_dir() / 'include'}, -) - -# add the testsuite -subdir('test') - -if install - module_id = meson.project_name() / fc_id + '-' + fc.version() - meson.add_install_script( - find_program(files('configure'/'install-mod.py')), - get_option('includedir') / module_id, - ) - - pkg = import('pkgconfig') - pkg.generate( - fftpack_lib, - description: 'fftpack', - subdirs: ['', module_id], - ) -endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 67d8837..cd88c3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,67 +1,210 @@ -# Locate the source directories -set(dir ${CMAKE_CURRENT_SOURCE_DIR}) -set(subdir "${dir}/fftpack") +#### Pre-process: .fpp -> .f90 via Fypp -# The source files -set(FFTPACK_SOURCES - ${subdir}/cfftb1.f90 - ${subdir}/cfftf1.f90 - ${subdir}/cffti1.f90 - ${subdir}/cosqb1.f90 - ${subdir}/cosqf1.f90 - ${subdir}/dcosqb.f90 - ${subdir}/dcosqf.f90 - ${subdir}/dcosqi.f90 - ${subdir}/dcost.f90 - ${subdir}/dcosti.f90 - ${subdir}/dfftb.f90 - ${subdir}/dfftf.f90 - ${subdir}/dffti.f90 - ${subdir}/dsinqb.f90 - ${subdir}/dsinqf.f90 - ${subdir}/dsinqi.f90 - ${subdir}/dsint.f90 - ${subdir}/dsinti.f90 - ${subdir}/dzfftb.f90 - ${subdir}/dzfftf.f90 - ${subdir}/dzffti.f90 - ${subdir}/ezfft1.f90 - ${dir}/fftpack.f90 - ${subdir}/fftpack_dct.f90 - ${subdir}/fftpack_fft.f90 - ${subdir}/fftpack_fftshift.f90 - ${subdir}/fftpack_ifft.f90 - ${subdir}/fftpack_ifftshift.f90 - ${subdir}/fftpack_irfft.f90 - ${subdir}/fftpack_rfft.f90 - ${subdir}/fftpack_utils.f90 - ${subdir}/passb.f90 - ${subdir}/passb2.f90 - ${subdir}/passb3.f90 - ${subdir}/passb4.f90 - ${subdir}/passb5.f90 - ${subdir}/passf.f90 - ${subdir}/passf2.f90 - ${subdir}/passf3.f90 - ${subdir}/passf4.f90 - ${subdir}/passf5.f90 - ${subdir}/radb2.f90 - ${subdir}/radb3.f90 - ${subdir}/radb4.f90 - ${subdir}/radb5.f90 - ${subdir}/radbg.f90 - ${subdir}/radf2.f90 - ${subdir}/radf3.f90 - ${subdir}/radf4.f90 - ${subdir}/radf5.f90 - ${subdir}/radfg.f90 - ${subdir}/rfftb1.f90 - ${subdir}/rfftf1.f90 - ${subdir}/rffti1.f90 - ${subdir}/rk.f90 - ${subdir}/sint1.f90 - ${subdir}/zfftb.f90 - ${subdir}/zfftf.f90 - ${subdir}/zffti.f90 +# Create a list of the files to be preprocessed +# set(fppFiles +# stdlib_ascii.fypp +# stdlib_bitsets.fypp +# stdlib_bitsets_64.fypp +# stdlib_bitsets_large.fypp +# stdlib_codata_type.fypp +# stdlib_constants.fypp +# stdlib_error.fypp +# stdlib_hash_32bit.fypp +# stdlib_hash_32bit_fnv.fypp +# stdlib_hash_32bit_nm.fypp +# stdlib_hash_32bit_water.fypp +# stdlib_hash_64bit.fypp +# stdlib_hash_64bit_fnv.fypp +# stdlib_hash_64bit_pengy.fypp +# stdlib_hash_64bit_spookyv2.fypp +# stdlib_intrinsics_dot_product.fypp +# stdlib_intrinsics_sum.fypp +# stdlib_intrinsics.fypp +# stdlib_io.fypp +# stdlib_io_npy.fypp +# stdlib_io_npy_load.fypp +# stdlib_io_npy_save.fypp +# stdlib_kinds.fypp +# stdlib_linalg.fypp +# stdlib_linalg_diag.fypp +# stdlib_linalg_least_squares.fypp +# stdlib_linalg_outer_product.fypp +# stdlib_linalg_kronecker.fypp +# stdlib_linalg_cross_product.fypp +# stdlib_linalg_eigenvalues.fypp +# stdlib_linalg_solve.fypp +# stdlib_linalg_determinant.fypp +# stdlib_linalg_qr.fypp +# stdlib_linalg_inverse.fypp +# stdlib_linalg_pinv.fypp +# stdlib_linalg_norms.fypp +# stdlib_linalg_state.fypp +# stdlib_linalg_svd.fypp +# stdlib_linalg_cholesky.fypp +# stdlib_linalg_schur.fypp +# stdlib_optval.fypp +# stdlib_selection.fypp +# stdlib_sorting.fypp +# stdlib_sorting_ord_sort.fypp +# stdlib_sorting_sort.fypp +# stdlib_sorting_sort_index.fypp +# stdlib_sparse_constants.fypp +# stdlib_sparse_conversion.fypp +# stdlib_sparse_kinds.fypp +# stdlib_sparse_spmv.fypp +# stdlib_specialfunctions_activations.fypp +# stdlib_specialfunctions_gamma.fypp +# stdlib_specialfunctions.fypp +# stdlib_specialmatrices.fypp +# stdlib_specialmatrices_tridiagonal.fypp +# stdlib_stats.fypp +# stdlib_stats_corr.fypp +# stdlib_stats_cov.fypp +# stdlib_stats_mean.fypp +# stdlib_stats_median.fypp +# stdlib_stats_moment.fypp +# stdlib_stats_moment_all.fypp +# stdlib_stats_moment_mask.fypp +# stdlib_stats_moment_scalar.fypp +# stdlib_stats_distribution_uniform.fypp +# stdlib_stats_distribution_normal.fypp +# stdlib_stats_distribution_exponential.fypp +# stdlib_stats_var.fypp +# stdlib_quadrature.fypp +# stdlib_quadrature_trapz.fypp +# stdlib_quadrature_simps.fypp +# stdlib_random.fypp +# stdlib_math.fypp +# stdlib_math_linspace.fypp +# stdlib_math_logspace.fypp +# stdlib_math_arange.fypp +# stdlib_math_is_close.fypp +# stdlib_math_all_close.fypp +# stdlib_math_diff.fypp +# stdlib_math_meshgrid.fypp +# stdlib_str2num.fypp +# stdlib_string_type.fypp +# stdlib_string_type_constructor.fypp +# stdlib_strings_to_string.fypp +# stdlib_strings.fypp +# stdlib_version.fypp +# ) + +# Preprocessed files to contain preprocessor directives -> .F90 +# set(cppFiles +# stdlib_linalg_constants.fypp +# stdlib_linalg_blas.fypp +# stdlib_linalg_lapack.fypp +# ) + +# fypp_f90("${fyppFlags}" "${fppFiles}" outFiles) +# fypp_f90pp("${fyppFlags}" "${cppFiles}" outPreprocFiles) + +set(SRC + cfftb1.f90 + cfftf1.f90 + cffti1.f90 + cosqb1.f90 + cosqf1.f90 + dcosqb.f90 + dcosqf.f90 + dcosqi.f90 + dcost.f90 + dcosti.f90 + dfftb.f90 + dfftf.f90 + dffti.f90 + dsinqb.f90 + dsinqf.f90 + dsinqi.f90 + dsint.f90 + dsinti.f90 + dzfftb.f90 + dzfftf.f90 + dzffti.f90 + ezfft1.f90 + fftpack_dct.f90 + fftpack.f90 + fftpack_fft.f90 + fftpack_fftshift.f90 + fftpack_ifft.f90 + fftpack_ifftshift.f90 + fftpack_irfft.f90 + fftpack_rfft.f90 + fftpack_utils.f90 + passb2.f90 + passb3.f90 + passb4.f90 + passb5.f90 + passb.f90 + passf2.f90 + passf3.f90 + passf4.f90 + passf5.f90 + passf.f90 + radb2.f90 + radb3.f90 + radb4.f90 + radb5.f90 + radbg.f90 + radf2.f90 + radf3.f90 + radf4.f90 + radf5.f90 + radfg.f90 + rfftb1.f90 + rfftf1.f90 + rffti1.f90 + rk.f90 + sint1.f90 + zfftb.f90 + zfftf.f90 + zffti.f90 + ${outFiles} + ${outPreprocFiles} +) + +add_library(${PROJECT_NAME} ${SRC}) + +# # Link to BLAS and LAPACK +# if(BLAS_FOUND AND LAPACK_FOUND) +# target_link_libraries(${PROJECT_NAME} "BLAS::BLAS") +# target_link_libraries(${PROJECT_NAME} "LAPACK::LAPACK") +# endif() + +set_target_properties( + ${PROJECT_NAME} + PROPERTIES + POSITION_INDEPENDENT_CODE ON + WINDOWS_EXPORT_ALL_SYMBOLS ON +) + +if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 10.0) + target_compile_options( + ${PROJECT_NAME} + PRIVATE + $<$:-fno-range-check> + ) +endif() + +set(LIB_MOD_DIR ${CMAKE_CURRENT_BINARY_DIR}/mod_files/) +# We need the module directory before we finish the configure stage since the +# build interface might resolve before the module directory is generated by CMake +if(NOT EXISTS "${LIB_MOD_DIR}") + file(MAKE_DIRECTORY "${LIB_MOD_DIR}") +endif() + +set_target_properties(${PROJECT_NAME} PROPERTIES + Fortran_MODULE_DIRECTORY ${LIB_MOD_DIR}) +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $ +) + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) -set(FFTPACK_SOURCES ${FFTPACK_SOURCES} PARENT_SCOPE) +install(DIRECTORY ${LIB_MOD_DIR} DESTINATION "${CMAKE_INSTALL_MODULEDIR}") diff --git a/src/fftpack/cfftb1.f90 b/src/cfftb1.f90 similarity index 100% rename from src/fftpack/cfftb1.f90 rename to src/cfftb1.f90 diff --git a/src/fftpack/cfftf1.f90 b/src/cfftf1.f90 similarity index 100% rename from src/fftpack/cfftf1.f90 rename to src/cfftf1.f90 diff --git a/src/fftpack/cffti1.f90 b/src/cffti1.f90 similarity index 100% rename from src/fftpack/cffti1.f90 rename to src/cffti1.f90 diff --git a/src/fftpack/cosqb1.f90 b/src/cosqb1.f90 similarity index 100% rename from src/fftpack/cosqb1.f90 rename to src/cosqb1.f90 diff --git a/src/fftpack/cosqf1.f90 b/src/cosqf1.f90 similarity index 100% rename from src/fftpack/cosqf1.f90 rename to src/cosqf1.f90 diff --git a/src/fftpack/dcosqb.f90 b/src/dcosqb.f90 similarity index 100% rename from src/fftpack/dcosqb.f90 rename to src/dcosqb.f90 diff --git a/src/fftpack/dcosqf.f90 b/src/dcosqf.f90 similarity index 100% rename from src/fftpack/dcosqf.f90 rename to src/dcosqf.f90 diff --git a/src/fftpack/dcosqi.f90 b/src/dcosqi.f90 similarity index 100% rename from src/fftpack/dcosqi.f90 rename to src/dcosqi.f90 diff --git a/src/fftpack/dcost.f90 b/src/dcost.f90 similarity index 100% rename from src/fftpack/dcost.f90 rename to src/dcost.f90 diff --git a/src/fftpack/dcosti.f90 b/src/dcosti.f90 similarity index 100% rename from src/fftpack/dcosti.f90 rename to src/dcosti.f90 diff --git a/src/fftpack/dfftb.f90 b/src/dfftb.f90 similarity index 100% rename from src/fftpack/dfftb.f90 rename to src/dfftb.f90 diff --git a/src/fftpack/dfftf.f90 b/src/dfftf.f90 similarity index 100% rename from src/fftpack/dfftf.f90 rename to src/dfftf.f90 diff --git a/src/fftpack/dffti.f90 b/src/dffti.f90 similarity index 100% rename from src/fftpack/dffti.f90 rename to src/dffti.f90 diff --git a/src/fftpack/dsinqb.f90 b/src/dsinqb.f90 similarity index 100% rename from src/fftpack/dsinqb.f90 rename to src/dsinqb.f90 diff --git a/src/fftpack/dsinqf.f90 b/src/dsinqf.f90 similarity index 100% rename from src/fftpack/dsinqf.f90 rename to src/dsinqf.f90 diff --git a/src/fftpack/dsinqi.f90 b/src/dsinqi.f90 similarity index 100% rename from src/fftpack/dsinqi.f90 rename to src/dsinqi.f90 diff --git a/src/fftpack/dsint.f90 b/src/dsint.f90 similarity index 100% rename from src/fftpack/dsint.f90 rename to src/dsint.f90 diff --git a/src/fftpack/dsinti.f90 b/src/dsinti.f90 similarity index 100% rename from src/fftpack/dsinti.f90 rename to src/dsinti.f90 diff --git a/src/fftpack/dzfftb.f90 b/src/dzfftb.f90 similarity index 100% rename from src/fftpack/dzfftb.f90 rename to src/dzfftb.f90 diff --git a/src/fftpack/dzfftf.f90 b/src/dzfftf.f90 similarity index 100% rename from src/fftpack/dzfftf.f90 rename to src/dzfftf.f90 diff --git a/src/fftpack/dzffti.f90 b/src/dzffti.f90 similarity index 100% rename from src/fftpack/dzffti.f90 rename to src/dzffti.f90 diff --git a/src/fftpack/ezfft1.f90 b/src/ezfft1.f90 similarity index 100% rename from src/fftpack/ezfft1.f90 rename to src/ezfft1.f90 diff --git a/src/fftpack/meson.build b/src/fftpack/meson.build deleted file mode 100644 index f025567..0000000 --- a/src/fftpack/meson.build +++ /dev/null @@ -1,61 +0,0 @@ -srcs += files( - 'cfftb1.f90', - 'dsinqi.f90', - 'fftpack_utils.f90', - 'radbg.f90', - 'cfftf1.f90', - 'dsint.f90', - 'passb2.f90', - 'radf2.f90', - 'cffti1.f90', - 'dsinti.f90', - 'passb3.f90', - 'radf3.f90', - 'cosqb1.f90', - 'dzfftb.f90', - 'passb4.f90', - 'radf4.f90', - 'cosqf1.f90', - 'dzfftf.f90', - 'passb5.f90', - 'radf5.f90', - 'dcosqb.f90', - 'dzffti.f90', - 'passb.f90', - 'radfg.f90', - 'dcosqf.f90', - 'ezfft1.f90', - 'passf2.f90', - 'rfftb1.f90', - 'dcosqi.f90', - 'fftpack_dct.f90', - 'passf3.f90', - 'rfftf1.f90', - 'dcost.f90', - 'fftpack.f90', - 'passf4.f90', - 'rffti1.f90', - 'dcosti.f90', - 'fftpack_fft.f90', - 'passf5.f90', - 'rk.f90', - 'dfftb.f90', - 'fftpack_fftshift.f90', - 'passf.f90', - 'sint1.f90', - 'dfftf.f90', - 'fftpack_ifft.f90', - 'radb2.f90', - 'zfftb.f90', - 'dffti.f90', - 'fftpack_ifftshift.f90', - 'radb3.f90', - 'zfftf.f90', - 'dsinqb.f90', - 'fftpack_irfft.f90', - 'radb4.f90', - 'zffti.f90', - 'dsinqf.f90', - 'fftpack_rfft.f90', - 'radb5.f90' -) diff --git a/src/fftpack/fftpack_dct.f90 b/src/fftpack_dct.f90 similarity index 100% rename from src/fftpack/fftpack_dct.f90 rename to src/fftpack_dct.f90 diff --git a/src/fftpack/fftpack_fft.f90 b/src/fftpack_fft.f90 similarity index 100% rename from src/fftpack/fftpack_fft.f90 rename to src/fftpack_fft.f90 diff --git a/src/fftpack/fftpack_fftshift.f90 b/src/fftpack_fftshift.f90 similarity index 100% rename from src/fftpack/fftpack_fftshift.f90 rename to src/fftpack_fftshift.f90 diff --git a/src/fftpack/fftpack_ifft.f90 b/src/fftpack_ifft.f90 similarity index 100% rename from src/fftpack/fftpack_ifft.f90 rename to src/fftpack_ifft.f90 diff --git a/src/fftpack/fftpack_ifftshift.f90 b/src/fftpack_ifftshift.f90 similarity index 100% rename from src/fftpack/fftpack_ifftshift.f90 rename to src/fftpack_ifftshift.f90 diff --git a/src/fftpack/fftpack_irfft.f90 b/src/fftpack_irfft.f90 similarity index 100% rename from src/fftpack/fftpack_irfft.f90 rename to src/fftpack_irfft.f90 diff --git a/src/fftpack/fftpack_rfft.f90 b/src/fftpack_rfft.f90 similarity index 100% rename from src/fftpack/fftpack_rfft.f90 rename to src/fftpack_rfft.f90 diff --git a/src/fftpack/fftpack_utils.f90 b/src/fftpack_utils.f90 similarity index 100% rename from src/fftpack/fftpack_utils.f90 rename to src/fftpack_utils.f90 diff --git a/src/fftpack/passb.f90 b/src/passb.f90 similarity index 100% rename from src/fftpack/passb.f90 rename to src/passb.f90 diff --git a/src/fftpack/passb2.f90 b/src/passb2.f90 similarity index 100% rename from src/fftpack/passb2.f90 rename to src/passb2.f90 diff --git a/src/fftpack/passb3.f90 b/src/passb3.f90 similarity index 100% rename from src/fftpack/passb3.f90 rename to src/passb3.f90 diff --git a/src/fftpack/passb4.f90 b/src/passb4.f90 similarity index 100% rename from src/fftpack/passb4.f90 rename to src/passb4.f90 diff --git a/src/fftpack/passb5.f90 b/src/passb5.f90 similarity index 100% rename from src/fftpack/passb5.f90 rename to src/passb5.f90 diff --git a/src/fftpack/passf.f90 b/src/passf.f90 similarity index 100% rename from src/fftpack/passf.f90 rename to src/passf.f90 diff --git a/src/fftpack/passf2.f90 b/src/passf2.f90 similarity index 100% rename from src/fftpack/passf2.f90 rename to src/passf2.f90 diff --git a/src/fftpack/passf3.f90 b/src/passf3.f90 similarity index 100% rename from src/fftpack/passf3.f90 rename to src/passf3.f90 diff --git a/src/fftpack/passf4.f90 b/src/passf4.f90 similarity index 100% rename from src/fftpack/passf4.f90 rename to src/passf4.f90 diff --git a/src/fftpack/passf5.f90 b/src/passf5.f90 similarity index 100% rename from src/fftpack/passf5.f90 rename to src/passf5.f90 diff --git a/src/fftpack/radb2.f90 b/src/radb2.f90 similarity index 100% rename from src/fftpack/radb2.f90 rename to src/radb2.f90 diff --git a/src/fftpack/radb3.f90 b/src/radb3.f90 similarity index 100% rename from src/fftpack/radb3.f90 rename to src/radb3.f90 diff --git a/src/fftpack/radb4.f90 b/src/radb4.f90 similarity index 100% rename from src/fftpack/radb4.f90 rename to src/radb4.f90 diff --git a/src/fftpack/radb5.f90 b/src/radb5.f90 similarity index 100% rename from src/fftpack/radb5.f90 rename to src/radb5.f90 diff --git a/src/fftpack/radbg.f90 b/src/radbg.f90 similarity index 100% rename from src/fftpack/radbg.f90 rename to src/radbg.f90 diff --git a/src/fftpack/radf2.f90 b/src/radf2.f90 similarity index 100% rename from src/fftpack/radf2.f90 rename to src/radf2.f90 diff --git a/src/fftpack/radf3.f90 b/src/radf3.f90 similarity index 100% rename from src/fftpack/radf3.f90 rename to src/radf3.f90 diff --git a/src/fftpack/radf4.f90 b/src/radf4.f90 similarity index 100% rename from src/fftpack/radf4.f90 rename to src/radf4.f90 diff --git a/src/fftpack/radf5.f90 b/src/radf5.f90 similarity index 100% rename from src/fftpack/radf5.f90 rename to src/radf5.f90 diff --git a/src/fftpack/radfg.f90 b/src/radfg.f90 similarity index 100% rename from src/fftpack/radfg.f90 rename to src/radfg.f90 diff --git a/src/fftpack/rfftb1.f90 b/src/rfftb1.f90 similarity index 100% rename from src/fftpack/rfftb1.f90 rename to src/rfftb1.f90 diff --git a/src/fftpack/rfftf1.f90 b/src/rfftf1.f90 similarity index 100% rename from src/fftpack/rfftf1.f90 rename to src/rfftf1.f90 diff --git a/src/fftpack/rffti1.f90 b/src/rffti1.f90 similarity index 100% rename from src/fftpack/rffti1.f90 rename to src/rffti1.f90 diff --git a/src/fftpack/rk.f90 b/src/rk.f90 similarity index 100% rename from src/fftpack/rk.f90 rename to src/rk.f90 diff --git a/src/fftpack/sint1.f90 b/src/sint1.f90 similarity index 100% rename from src/fftpack/sint1.f90 rename to src/sint1.f90 diff --git a/src/fftpack/zfftb.f90 b/src/zfftb.f90 similarity index 100% rename from src/fftpack/zfftb.f90 rename to src/zfftb.f90 diff --git a/src/fftpack/zfftf.f90 b/src/zfftf.f90 similarity index 100% rename from src/fftpack/zfftf.f90 rename to src/zfftf.f90 diff --git a/src/fftpack/zffti.f90 b/src/zffti.f90 similarity index 100% rename from src/fftpack/zffti.f90 rename to src/zffti.f90 diff --git a/subprojects/test-drive.wrap b/subprojects/test-drive.wrap deleted file mode 100644 index ddc1061..0000000 --- a/subprojects/test-drive.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -directory = test-drive -url = https://github.com/fortran-lang/test-drive -revision = v0.4.0 \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cf5c570..bee0a4c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,24 +1,32 @@ -macro(build_tests testname) - add_executable(${testname} ${ARGN}) - link_library(${testname} fftpack ${PROJECT_INCLUDE_DIR}) - link_library(${testname} test-drive ${test-drive_INCLUDE_DIR}) - add_test( - NAME ${testname} - WORKING_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} - COMMAND $ - ) -endmacro() +if (NOT TARGET "test-drive::test-drive") + find_package("test-drive" REQUIRED) +endif() -set(FFTPACK_TEST_SOURCES - test_fftpack_dct.f90 - test_fftpack_fft.f90 - test_fftpack_rfft.f90 - test_fftpack_utils.f90 - test_fftpack.f90 -) +if(WIN32) + if(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") + add_link_options(/Qoption,link,/STACK:8388608) + elseif(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) + add_link_options(-Wl,--stack,8388608) + endif() +endif() -# Run the original FFTPACK test -build_tests(fftpack_original_tests tstfft.f) +macro(ADDTEST name) + add_executable(test_${name} test_${name}.f90) + target_link_libraries(test_${name} "${PROJECT_NAME}" "test-drive::test-drive") + add_test(NAME ${name} + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endmacro(ADDTEST) -# Run the additional FFTPACK tests -build_tests(fftpack_tests ${FFTPACK_TEST_SOURCES}) +macro(ADDTESTPP name) + add_executable(test_${name} test_${name}.F90) + target_link_libraries(test_${name} "${PROJECT_NAME}" "test-drive::test-drive") + add_test(NAME ${name} + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endmacro(ADDTESTPP) + +ADDTEST(fftpack_fft) +ADDTEST(fftpack_rfft) +ADDTEST(fftpack_dct) +ADDTEST(fftpack_utils) diff --git a/test/meson.build b/test/meson.build deleted file mode 100644 index 14528fe..0000000 --- a/test/meson.build +++ /dev/null @@ -1,25 +0,0 @@ -testdrive_dep = dependency('test-drive', fallback: ['test-drive', 'testdrive_dep']) - -tests = [ - 'fft', - 'rfft', - 'dct', - 'utils' -] - -test_srcs = files( - 'test_fftpack.f90' -) -foreach t : tests - test_srcs += files('test_fftpack_@0@.f90'.format(t.underscorify())) -endforeach - -tester = executable( - 'tester', - sources: test_srcs, - dependencies: [fftpack_dep, testdrive_dep] -) - -foreach t : tests - test(t, tester, args: t) -endforeach diff --git a/test/test_fftpack.f90 b/test/test_fftpack.f90 deleted file mode 100644 index b3b4f47..0000000 --- a/test/test_fftpack.f90 +++ /dev/null @@ -1,32 +0,0 @@ -program test_fftpack - use, intrinsic :: iso_fortran_env, only: error_unit - use testdrive, only: run_testsuite, new_testsuite, testsuite_type - use test_fftpack_fft, only: collect_fft - use test_fftpack_rfft, only: collect_rfft - use test_fftpack_dct, only: collect_dct - use test_fftpack_utils, only: collect_utils - implicit none - integer :: stat, is - type(testsuite_type), allocatable :: testsuites(:) - character(len=*), parameter :: fmt = '("#", *(1x, a))' - - stat = 0 - - testsuites = [ & - new_testsuite("fft", collect_fft), & - new_testsuite("rfft", collect_rfft), & - new_testsuite("dct", collect_dct), & - new_testsuite("utils", collect_utils) & - ] - - do is = 1, size(testsuites) - write (error_unit, fmt) "Testing:", testsuites(is)%name - call run_testsuite(testsuites(is)%collect, error_unit, stat) - end do - - if (stat > 0) then - write (error_unit, '(i0, 1x, a)') stat, "test(s) failed!" - error stop - end if - -end program test_fftpack diff --git a/test/test_fftpack_dct.f90 b/test/test_fftpack_dct.f90 index cb4cf49..c1f1861 100644 --- a/test/test_fftpack_dct.f90 +++ b/test/test_fftpack_dct.f90 @@ -1,155 +1,181 @@ module test_fftpack_dct - use fftpack - use testdrive, only: new_unittest, unittest_type, error_type, check - implicit none - private + use fftpack + use testdrive, only: new_unittest, unittest_type, error_type, check + implicit none + private - public :: collect_dct + public :: collect_dct contains - !> Collect all exported unit tests - subroutine collect_dct(testsuite) - !> Collection of tests - type(unittest_type), allocatable, intent(out) :: testsuite(:) - - testsuite = [ & - new_unittest("classic-dct-API", test_classic_dct), & - new_unittest("modernized-dct-API", test_modernized_dct), & - new_unittest("modernized-idct-API", test_modernized_idct) & - ] - - end subroutine collect_dct - - subroutine test_classic_dct(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: w(3*4 + 15), w2(3*4 + 15) - real(kind=rk) :: x(4) = [1, 2, 3, 4] - real(kind=rk) :: x2(4) - real(kind=rk) :: eps = 1.0e-10_rk - - x2 = x - call dcosti(4, w) - call dcost(4, x, w) - call check(error, sum(abs(x - [real(kind=rk) :: 15, -4, 0, -1.0000000000000009_rk])) < eps, & - "`dcost` failed.") - if (allocated(error)) return - - call dct_t1i(4, w2) - call dct_t1(4, x2, w2) - call check(error, maxval(abs(x2-x)) < eps, "dct_t1 failed") - if (allocated(error)) return - - call dcost(4, x, w) - call check(error, sum(abs(x/(2*3) - [real(kind=rk) :: 1, 2, 3, 4])) < eps, & - "2nd `dcost` failed.") - if (allocated(error)) return - - call dct_t1(4, x2, w2) - call check(error, maxval(abs(x2-x)) < eps, "2nd dct_t1 failed") - if (allocated(error)) return - - x = [1, 2, 3, 4] - x2 = x - call dcosqi(4, w) - call dcosqf(4, x, w) - call check(error, sum(abs(x - [11.999626276085150_rk, -9.1029432177492193_rk, & - 2.6176618435106480_rk, -1.5143449018465791_rk])) < eps, & - "`dcosqf` failed.") - if (allocated(error)) return - - call dct_t23i(4, w2) - call dct_t3(4, x2, w2) - call check(error, maxval(abs(x2-x)) < eps, "dct_t3 failed") - if (allocated(error)) return - - call dcosqb(4, x, w) - call check(error, sum(abs(x/(4*4) - [real(kind=rk) :: 1, 2, 3, 4])) < eps, & - "`dcosqb` failed.") - if (allocated(error)) return - - call dct_t2(4, x2, w2) - call check(error, maxval(abs(x2-x)) < eps, "dct_t2 failed") - - end subroutine test_classic_dct - - subroutine test_modernized_dct(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: eps = 1.0e-10_rk - real(kind=rk) :: x(3) = [9, -9, 3] - - ! DCT-1 - call check(error, sum(abs(dct(x,2,1) - [0.0_rk, 18.0_rk])) < eps, "`dct(x,2,1)` failed.") - if (allocated(error)) return - call check(error, sum(abs(dct(x,3,1) - dct(x,type=1))) < eps, "`dct(x,3,1)` failed.") - if (allocated(error)) return - call check(error, sum(abs(dct(x,4,1) - [real(kind=rk) :: -3, -3.0000000000000036_rk, 15, 33])) < eps,& - "`dct(x,4,1)` failed.") - !DCT-2 - x = [9, -9, 3] - call check(error, sum(abs(dct(x,3,2) - [12.0_rk, 20.784609690826525_rk, 60.0_rk])) < eps,& - "`dct(x,3,2)` failed.") - call check(error, sum(abs(dct(x,3) - [12.0_rk, 20.784609690826525_rk, 60.0_rk])) < eps,& - "`dct(x,3)` failed.") - call check(error, sum(abs(dct(x,4,2) - [12.0_rk, 14.890858416882008_rk, 42.426406871192853_rk,& - 58.122821125684993_rk])) < eps, "`dct(x,4,2)` failed.") - - ! DCT-3 - x = [9, -9, 3] - call check(error, sum(abs(dct(x,2,3) - [-3.7279220613578570_rk, 21.727922061357859_rk])) < eps, & - "`dct(x,2,3)` failed.") - if (allocated(error)) return - call check(error, sum(abs(dct(x,3,3) - dct(x,type=3))) < eps, & - "`dct(x,3,3)` failed.") - if (allocated(error)) return - call check(error, sum(abs(dct(x,4,3) - [-3.3871908980838743_rk, -2.1309424696909023_rk, & - 11.645661095452331_rk, 29.872472272322447_rk])) < eps, & - "`dct(x,4,3)` failed.") - - end subroutine test_modernized_dct - - subroutine test_modernized_idct(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: eps = 1.0e-10_rk - real(kind=rk) :: x(4) = [1, 2, 3, 4] - - ! inverse DCT-1 - call check(error, sum(abs(idct(dct(x,type=1),type=1)/(2*3) - x)) < eps, & - "`idct(dct(x,type=1),type=1)/(2*3)` failed.") - if (allocated(error)) return - call check(error, sum(abs(idct(dct(x,type=1), 2, 1)/(2*1) - [5.5_rk, 9.5_rk])) < eps, & - "`idct(dct(x,type=1), 2, 1)/(2*1)` failed.") - if (allocated(error)) return - call check(error, sum(abs(idct(dct(x,2,1), 4, 1)/(2*3) - [0.16666666666666666_rk, 0.33333333333333331_rk, & - 0.66666666666666663_rk, 0.83333333333333315_rk])) < eps, & - "`idct(dct(x,2,1), 4, 1)/(2*3)` failed.") - - ! inverse DCT-2 - x = [1, 2, 3, 4] - call check(error, sum(abs(idct(dct(x,type=2))/(4*4) - x)) < eps, & - "`idct(dct(x, type=2))/(4*4)` failed.") - if (allocated(error)) return - call check(error, sum(abs(idct(dct(x,type=2),n=2) - [22.156460020898692_rk, 57.843539979101308_rk])) < eps, & - "`idct(dct(x, type=2),n=2)` failed.") - if (allocated(error)) return - call check(error, sum(abs(idct(dct(x,n=2,type=2),n=4) - [6.7737481404944937_rk, 9.8352155994152106_rk, & - 14.164784400584789_rk, 17.226251859505506_rk])) < eps, & - "`idct(dct(x, n=2, type=2), n=4)` failed.") - - ! inverse DCT-3 - x = [1, 2, 3, 4] - call check(error, sum(abs(idct(dct(x,type=3),type=3)/(4*4) - x)) < eps, & - "`idct(dct(x, type=3), type=3)/(4*4)` failed.") - if (allocated(error)) return - call check(error, sum(abs(idct(dct(x,type=3),n=2,type=3)/(4*2) - & - [1.4483415291679655_rk, 7.4608849947753271_rk])) < eps, & - "`idct(dct(x, type=3), n=2, type=3)/(4*2)` failed.") - if (allocated(error)) return - call check(error, sum(abs(idct(dct(x,n=2,type=3),n=4,type=3)/(4*4) - & - [0.5_rk, 0.70932417358418376_rk, 1.0_rk, 0.78858050747473762_rk])) < eps, & - "`idct(dct(x, n=2, type=3), n=4, type=3)/(4*4)` failed.") - - end subroutine test_modernized_idct + !> Collect all exported unit tests + subroutine collect_dct(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("classic-dct-API", test_classic_dct), & + new_unittest("modernized-dct-API", test_modernized_dct), & + new_unittest("modernized-idct-API", test_modernized_idct) & + ] + + end subroutine collect_dct + + subroutine test_classic_dct(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: w(3*4 + 15), w2(3*4 + 15) + real(kind=rk) :: x(4) = [1, 2, 3, 4] + real(kind=rk) :: x2(4) + real(kind=rk) :: eps = 1.0e-10_rk + + x2 = x + call dcosti(4, w) + call dcost(4, x, w) + call check(error, sum(abs(x - [real(kind=rk) :: 15, -4, 0, -1.0000000000000009_rk])) < eps, & + "`dcost` failed.") + if (allocated(error)) return + + call dct_t1i(4, w2) + call dct_t1(4, x2, w2) + call check(error, maxval(abs(x2 - x)) < eps, "dct_t1 failed") + if (allocated(error)) return + + call dcost(4, x, w) + call check(error, sum(abs(x/(2*3) - [real(kind=rk) :: 1, 2, 3, 4])) < eps, & + "2nd `dcost` failed.") + if (allocated(error)) return + + call dct_t1(4, x2, w2) + call check(error, maxval(abs(x2 - x)) < eps, "2nd dct_t1 failed") + if (allocated(error)) return + + x = [1, 2, 3, 4] + x2 = x + call dcosqi(4, w) + call dcosqf(4, x, w) + call check(error, sum(abs(x - [11.999626276085150_rk, -9.1029432177492193_rk, & + 2.6176618435106480_rk, -1.5143449018465791_rk])) < eps, & + "`dcosqf` failed.") + if (allocated(error)) return + + call dct_t23i(4, w2) + call dct_t3(4, x2, w2) + call check(error, maxval(abs(x2 - x)) < eps, "dct_t3 failed") + if (allocated(error)) return + + call dcosqb(4, x, w) + call check(error, sum(abs(x/(4*4) - [real(kind=rk) :: 1, 2, 3, 4])) < eps, & + "`dcosqb` failed.") + if (allocated(error)) return + + call dct_t2(4, x2, w2) + call check(error, maxval(abs(x2 - x)) < eps, "dct_t2 failed") + + end subroutine test_classic_dct + + subroutine test_modernized_dct(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: eps = 1.0e-10_rk + real(kind=rk) :: x(3) = [9, -9, 3] + + ! DCT-1 + call check(error, sum(abs(dct(x, 2, 1) - [0.0_rk, 18.0_rk])) < eps, "`dct(x,2,1)` failed.") + if (allocated(error)) return + call check(error, sum(abs(dct(x, 3, 1) - dct(x, type=1))) < eps, "`dct(x,3,1)` failed.") + if (allocated(error)) return + call check(error, sum(abs(dct(x, 4, 1) - [real(kind=rk) :: -3, -3.0000000000000036_rk, 15, 33])) < eps, & + "`dct(x,4,1)` failed.") + !DCT-2 + x = [9, -9, 3] + call check(error, sum(abs(dct(x, 3, 2) - [12.0_rk, 20.784609690826525_rk, 60.0_rk])) < eps, & + "`dct(x,3,2)` failed.") + call check(error, sum(abs(dct(x, 3) - [12.0_rk, 20.784609690826525_rk, 60.0_rk])) < eps, & + "`dct(x,3)` failed.") + call check(error, sum(abs(dct(x, 4, 2) - [12.0_rk, 14.890858416882008_rk, 42.426406871192853_rk, & + 58.122821125684993_rk])) < eps, "`dct(x,4,2)` failed.") + + ! DCT-3 + x = [9, -9, 3] + call check(error, sum(abs(dct(x, 2, 3) - [-3.7279220613578570_rk, 21.727922061357859_rk])) < eps, & + "`dct(x,2,3)` failed.") + if (allocated(error)) return + call check(error, sum(abs(dct(x, 3, 3) - dct(x, type=3))) < eps, & + "`dct(x,3,3)` failed.") + if (allocated(error)) return + call check(error, sum(abs(dct(x, 4, 3) - [-3.3871908980838743_rk, -2.1309424696909023_rk, & + 11.645661095452331_rk, 29.872472272322447_rk])) < eps, & + "`dct(x,4,3)` failed.") + + end subroutine test_modernized_dct + + subroutine test_modernized_idct(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: eps = 1.0e-10_rk + real(kind=rk) :: x(4) = [1, 2, 3, 4] + + ! inverse DCT-1 + call check(error, sum(abs(idct(dct(x, type=1), type=1)/(2*3) - x)) < eps, & + "`idct(dct(x,type=1),type=1)/(2*3)` failed.") + if (allocated(error)) return + call check(error, sum(abs(idct(dct(x, type=1), 2, 1)/(2*1) - [5.5_rk, 9.5_rk])) < eps, & + "`idct(dct(x,type=1), 2, 1)/(2*1)` failed.") + if (allocated(error)) return + call check(error, sum(abs(idct(dct(x, 2, 1), 4, 1)/(2*3) - [0.16666666666666666_rk, 0.33333333333333331_rk, & + 0.66666666666666663_rk, 0.83333333333333315_rk])) < eps, & + "`idct(dct(x,2,1), 4, 1)/(2*3)` failed.") + + ! inverse DCT-2 + x = [1, 2, 3, 4] + call check(error, sum(abs(idct(dct(x, type=2))/(4*4) - x)) < eps, & + "`idct(dct(x, type=2))/(4*4)` failed.") + if (allocated(error)) return + call check(error, sum(abs(idct(dct(x, type=2), n=2) - [22.156460020898692_rk, 57.843539979101308_rk])) < eps, & + "`idct(dct(x, type=2),n=2)` failed.") + if (allocated(error)) return + call check(error, sum(abs(idct(dct(x, n=2, type=2), n=4) - [6.7737481404944937_rk, 9.8352155994152106_rk, & + 14.164784400584789_rk, 17.226251859505506_rk])) < eps, & + "`idct(dct(x, n=2, type=2), n=4)` failed.") + + ! inverse DCT-3 + x = [1, 2, 3, 4] + call check(error, sum(abs(idct(dct(x, type=3), type=3)/(4*4) - x)) < eps, & + "`idct(dct(x, type=3), type=3)/(4*4)` failed.") + if (allocated(error)) return + call check(error, sum(abs(idct(dct(x, type=3), n=2, type=3)/(4*2) - & + [1.4483415291679655_rk, 7.4608849947753271_rk])) < eps, & + "`idct(dct(x, type=3), n=2, type=3)/(4*2)` failed.") + if (allocated(error)) return + call check(error, sum(abs(idct(dct(x, n=2, type=3), n=4, type=3)/(4*4) - & + [0.5_rk, 0.70932417358418376_rk, 1.0_rk, 0.78858050747473762_rk])) < eps, & + "`idct(dct(x, n=2, type=3), n=4, type=3)/(4*4)` failed.") + + end subroutine test_modernized_idct end module test_fftpack_dct + +program test_dct + use, intrinsic :: iso_fortran_env, only: error_unit + use testdrive, only: run_testsuite, new_testsuite, testsuite_type + use test_fftpack_dct, only: collect_dct + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("dct", collect_dct) & + ] + + do is = 1, size(testsuites) + write (error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write (error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if +end program diff --git a/test/test_fftpack_fft.f90 b/test/test_fftpack_fft.f90 index 9db72fc..14634f5 100644 --- a/test/test_fftpack_fft.f90 +++ b/test/test_fftpack_fft.f90 @@ -1,74 +1,99 @@ module test_fftpack_fft - use fftpack, only: rk, zffti, zfftf, zfftb, fft, ifft - use testdrive, only: new_unittest, unittest_type, error_type, check - implicit none - private + use fftpack, only: rk, zffti, zfftf, zfftb, fft, ifft + use testdrive, only: new_unittest, unittest_type, error_type, check + implicit none + private - public :: collect_fft + public :: collect_fft contains - !> Collect all exported unit tests - subroutine collect_fft(testsuite) - !> Collection of tests - type(unittest_type), allocatable, intent(out) :: testsuite(:) - - testsuite = [ & - new_unittest("classic-fft-API", test_classic_fft), & - new_unittest("modernized-fft-API", test_modernized_fft), & - new_unittest("modernized-ifft-API", test_modernized_ifft) & - ] - - end subroutine collect_fft - - subroutine test_classic_fft(error) - type(error_type), allocatable, intent(out) :: error - complex(kind=rk) :: x(4) = [1, 2, 3, 4] - real(kind=rk) :: w(31) - - call zffti(4, w) - call zfftf(4, x, w) - call check(error, all(x == [complex(kind=rk) ::(10, 0), (-2, 2), (-2, 0), (-2, -2)]), & - "`zfftf` failed.") - if (allocated(error)) return - call zfftb(4, x, w) - call check(error, all(x/4.0_rk == [complex(kind=rk) ::(1, 0), (2, 0), (3, 0), (4, 0)]), & - "`zfftb` failed.") - - end subroutine test_classic_fft - - subroutine test_modernized_fft(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: eps = 1.0e-10_rk - complex(kind=rk) :: x(3) = [1.0_rk, 2.0_rk, 3.0_rk] - - call check(error, sum(abs(fft(x, 2) - [(3.0_rk, 0.0_rk), (-1.0_rk, 0.0_rk)])) < eps, & - "`fft(x, 2)` failed.") - if (allocated(error)) return - call check(error, sum(abs(fft(x, 3) - fft(x))) < eps, & - "`fft(x, 3)` failed.") - if (allocated(error)) return - call check(error, sum(abs(fft(x, 4) - [(6.0_rk, 0.0_rk), (-2.0_rk, -2.0_rk), (2.0_rk, 0.0_rk), (-2.0_rk, 2.0_rk)])) < eps, & - "`fft(x, 4)` failed.") - - end subroutine test_modernized_fft - - subroutine test_modernized_ifft(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: eps = 1.0e-10_rk - complex(kind=rk) :: x(4) = [1, 2, 3, 4] - - call check(error, sum(abs(ifft(fft(x))/4.0_rk - [complex(kind=rk) :: 1, 2, 3, 4])) < eps, & - "`ifft(fft(x))/4.0_rk` failed.") - if (allocated(error)) return - call check(error, sum(abs(ifft(fft(x), 2) - [complex(kind=rk) ::(8, 2), (12, -2)])) < eps, & - "`ifft(fft(x), 2)` failed.") - if (allocated(error)) return - call check(error, sum(abs(ifft(fft(x, 2), 4) - [complex(kind=rk) ::(2, 0), (3, -1), (4, 0), (3, 1)])) < eps, & - "`ifft(fft(x, 2), 4)` failed.") - - end subroutine test_modernized_ifft - + !> Collect all exported unit tests + subroutine collect_fft(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("classic-fft-API", test_classic_fft), & + new_unittest("modernized-fft-API", test_modernized_fft), & + new_unittest("modernized-ifft-API", test_modernized_ifft) & + ] + + end subroutine collect_fft + + subroutine test_classic_fft(error) + type(error_type), allocatable, intent(out) :: error + complex(kind=rk) :: x(4) = [1, 2, 3, 4] + real(kind=rk) :: w(31) + + call zffti(4, w) + call zfftf(4, x, w) + call check(error, all(x == [complex(kind=rk) ::(10, 0), (-2, 2), (-2, 0), (-2, -2)]), & + "`zfftf` failed.") + if (allocated(error)) return + call zfftb(4, x, w) + call check(error, all(x/4.0_rk == [complex(kind=rk) ::(1, 0), (2, 0), (3, 0), (4, 0)]), & + "`zfftb` failed.") + + end subroutine test_classic_fft + + subroutine test_modernized_fft(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: eps = 1.0e-10_rk + complex(kind=rk) :: x(3) = [1.0_rk, 2.0_rk, 3.0_rk] + + call check(error, sum(abs(fft(x, 2) - [(3.0_rk, 0.0_rk), (-1.0_rk, 0.0_rk)])) < eps, & + "`fft(x, 2)` failed.") + if (allocated(error)) return + call check(error, sum(abs(fft(x, 3) - fft(x))) < eps, & + "`fft(x, 3)` failed.") + if (allocated(error)) return + call check(error, sum(abs(fft(x, 4) - [(6.0_rk, 0.0_rk), (-2.0_rk, -2.0_rk), (2.0_rk, 0.0_rk), (-2.0_rk, 2.0_rk)])) < eps, & + "`fft(x, 4)` failed.") + + end subroutine test_modernized_fft + + subroutine test_modernized_ifft(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: eps = 1.0e-10_rk + complex(kind=rk) :: x(4) = [1, 2, 3, 4] + + call check(error, sum(abs(ifft(fft(x))/4.0_rk - [complex(kind=rk) :: 1, 2, 3, 4])) < eps, & + "`ifft(fft(x))/4.0_rk` failed.") + if (allocated(error)) return + call check(error, sum(abs(ifft(fft(x), 2) - [complex(kind=rk) ::(8, 2), (12, -2)])) < eps, & + "`ifft(fft(x), 2)` failed.") + if (allocated(error)) return + call check(error, sum(abs(ifft(fft(x, 2), 4) - [complex(kind=rk) ::(2, 0), (3, -1), (4, 0), (3, 1)])) < eps, & + "`ifft(fft(x, 2), 4)` failed.") + + end subroutine test_modernized_ifft end module test_fftpack_fft + +program test_fft + use, intrinsic :: iso_fortran_env, only: error_unit + use testdrive, only: run_testsuite, new_testsuite, testsuite_type + use test_fftpack_fft, only: collect_fft + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("fft", collect_fft) & + ] + + do is = 1, size(testsuites) + write (error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write (error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if +end program diff --git a/test/test_fftpack_rfft.f90 b/test/test_fftpack_rfft.f90 index 6b7cc15..51b81d9 100644 --- a/test/test_fftpack_rfft.f90 +++ b/test/test_fftpack_rfft.f90 @@ -1,90 +1,116 @@ module test_fftpack_rfft - use fftpack, only: rk, dffti, dfftf, dfftb, rfft, irfft - use fftpack, only: dzffti, dzfftf, dzfftb - use testdrive, only: new_unittest, unittest_type, error_type, check - implicit none - private + use fftpack, only: rk, dffti, dfftf, dfftb, rfft, irfft + use fftpack, only: dzffti, dzfftf, dzfftb + use testdrive, only: new_unittest, unittest_type, error_type, check + implicit none + private - public :: collect_rfft + public :: collect_rfft contains - !> Collect all exported unit tests - subroutine collect_rfft(testsuite) - !> Collection of tests - type(unittest_type), allocatable, intent(out) :: testsuite(:) - - testsuite = [ & - new_unittest("classic-rfft-API", test_classic_rfft), & - new_unittest("modernized-rfft-API", test_modernized_rfft), & - new_unittest("modernized-irfft-API", test_modernized_irfft) & - ] - - end subroutine collect_rfft - - subroutine test_classic_rfft(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: x(4) = [1, 2, 3, 4] - real(kind=rk) :: w(31) - real(kind=rk) :: azero, a(4/2), b(4/2) - - call dffti(4, w) - call dfftf(4, x, w) - call check(error, all(x == [real(kind=rk) :: 10, -2, 2, -2]), & - "`dfftf` failed.") - if (allocated(error)) return - call dfftb(4, x, w) - call check(error, all(x/4.0_rk == [real(kind=rk) :: 1, 2, 3, 4]), & - "`dfftb` failed.") - if (allocated(error)) return - - x = [1, 2, 3, 4] - call dzffti(4, w) - call dzfftf(4, x, azero, a, b, w) - call check(error, azero == 2.5_rk, "dzfftf: azero == 2.5_rk failed.") - if (allocated(error)) return - call check(error, all(a == [-1.0_rk, -0.5_rk]), "dzfftf: all(a == [-1.0, -0.5]) failed.") - if (allocated(error)) return - call check(error, all(b == [-1.0_rk, 0.0_rk]), "dzfftf: all(b == [-1.0, 0.0]) failed.") - if (allocated(error)) return - - call dzfftb(4, x, azero, a, b, w) - call check(error, all(x == [real(kind=rk) :: 1, 2, 3, 4]), & - "dzfftb: all(x = [real(kind=rk) :: 1, 2, 3, 4]) failed.") - - end subroutine test_classic_rfft - - subroutine test_modernized_rfft(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: eps = 1.0e-10_rk - real(kind=rk) :: x(3) = [9, -9, 3] - - call check(error, sum(abs(rfft(x, 2) - [real(kind=rk) :: 0, 18])) < eps, & - "`rfft(x, 2)` failed.") - if (allocated(error)) return - call check(error, sum(abs(rfft(x, 3) - rfft(x))) < eps, & - "`rfft(x, 3)` failed.") - if (allocated(error)) return - call check(error, sum(abs(rfft(x, 4) - [real(kind=rk) :: 3, 6, 9, 21])) < eps, & - "`rfft(x, 4)` failed.") - - end subroutine test_modernized_rfft - - subroutine test_modernized_irfft(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: eps = 1.0e-10_rk - real(kind=rk) :: x(4) = [1, 2, 3, 4] - - call check(error, sum(abs(irfft(rfft(x))/4.0_rk - [real(kind=rk) :: 1, 2, 3, 4])) < eps, & - "`irfft(rfft(x))/4.0_rk` failed.") - if (allocated(error)) return - call check(error, sum(abs(irfft(rfft(x), 2) - [real(kind=rk) :: 8, 12])) < eps, & - "`irfft(rfft(x), 2)` failed.") - if (allocated(error)) return - call check(error, sum(abs(irfft(rfft(x, 2), 4) - [real(kind=rk) :: 1, 3, 5, 3])) < eps, & - "`irfft(rfft(x, 2), 4)` failed.") - - end subroutine test_modernized_irfft + !> Collect all exported unit tests + subroutine collect_rfft(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("classic-rfft-API", test_classic_rfft), & + new_unittest("modernized-rfft-API", test_modernized_rfft), & + new_unittest("modernized-irfft-API", test_modernized_irfft) & + ] + + end subroutine collect_rfft + + subroutine test_classic_rfft(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: x(4) = [1, 2, 3, 4] + real(kind=rk) :: w(31) + real(kind=rk) :: azero, a(4/2), b(4/2) + + call dffti(4, w) + call dfftf(4, x, w) + call check(error, all(x == [real(kind=rk) :: 10, -2, 2, -2]), & + "`dfftf` failed.") + if (allocated(error)) return + call dfftb(4, x, w) + call check(error, all(x/4.0_rk == [real(kind=rk) :: 1, 2, 3, 4]), & + "`dfftb` failed.") + if (allocated(error)) return + + x = [1, 2, 3, 4] + call dzffti(4, w) + call dzfftf(4, x, azero, a, b, w) + call check(error, azero == 2.5_rk, "dzfftf: azero == 2.5_rk failed.") + if (allocated(error)) return + call check(error, all(a == [-1.0_rk, -0.5_rk]), "dzfftf: all(a == [-1.0, -0.5]) failed.") + if (allocated(error)) return + call check(error, all(b == [-1.0_rk, 0.0_rk]), "dzfftf: all(b == [-1.0, 0.0]) failed.") + if (allocated(error)) return + + call dzfftb(4, x, azero, a, b, w) + call check(error, all(x == [real(kind=rk) :: 1, 2, 3, 4]), & + "dzfftb: all(x = [real(kind=rk) :: 1, 2, 3, 4]) failed.") + + end subroutine test_classic_rfft + + subroutine test_modernized_rfft(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: eps = 1.0e-10_rk + real(kind=rk) :: x(3) = [9, -9, 3] + + call check(error, sum(abs(rfft(x, 2) - [real(kind=rk) :: 0, 18])) < eps, & + "`rfft(x, 2)` failed.") + if (allocated(error)) return + call check(error, sum(abs(rfft(x, 3) - rfft(x))) < eps, & + "`rfft(x, 3)` failed.") + if (allocated(error)) return + call check(error, sum(abs(rfft(x, 4) - [real(kind=rk) :: 3, 6, 9, 21])) < eps, & + "`rfft(x, 4)` failed.") + + end subroutine test_modernized_rfft + + subroutine test_modernized_irfft(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: eps = 1.0e-10_rk + real(kind=rk) :: x(4) = [1, 2, 3, 4] + + call check(error, sum(abs(irfft(rfft(x))/4.0_rk - [real(kind=rk) :: 1, 2, 3, 4])) < eps, & + "`irfft(rfft(x))/4.0_rk` failed.") + if (allocated(error)) return + call check(error, sum(abs(irfft(rfft(x), 2) - [real(kind=rk) :: 8, 12])) < eps, & + "`irfft(rfft(x), 2)` failed.") + if (allocated(error)) return + call check(error, sum(abs(irfft(rfft(x, 2), 4) - [real(kind=rk) :: 1, 3, 5, 3])) < eps, & + "`irfft(rfft(x, 2), 4)` failed.") + + end subroutine test_modernized_irfft end module test_fftpack_rfft + +program test_rfft + use, intrinsic :: iso_fortran_env, only: error_unit + use testdrive, only: run_testsuite, new_testsuite, testsuite_type + use test_fftpack_rfft, only: collect_rfft + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("rfft", collect_rfft) & + ] + + do is = 1, size(testsuites) + write (error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write (error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if +end program diff --git a/test/test_fftpack_utils.f90 b/test/test_fftpack_utils.f90 index 83fdfeb..ed7438c 100644 --- a/test/test_fftpack_utils.f90 +++ b/test/test_fftpack_utils.f90 @@ -1,160 +1,186 @@ module test_fftpack_utils - use fftpack, only: rk, fft, ifft, fftshift, ifftshift, fftfreq, rfftfreq - use testdrive, only: new_unittest, unittest_type, error_type, check - implicit none - private + use fftpack, only: rk, fft, ifft, fftshift, ifftshift, fftfreq, rfftfreq + use testdrive, only: new_unittest, unittest_type, error_type, check + implicit none + private - public :: collect_utils + public :: collect_utils contains - !> Collect all exported unit tests - subroutine collect_utils(testsuite) - !> Collection of tests - type(unittest_type), allocatable, intent(out) :: testsuite(:) - - testsuite = [ & - new_unittest("fftshift_complex", test_fftshift_complex), & - new_unittest("fftshift_real", test_fftshift_real), & - new_unittest("ifftshift_complex", test_fftshift_complex), & - new_unittest("ifftshift_real", test_fftshift_real), & - new_unittest("fftfreq_1", test_fftfreq_1), & - new_unittest("fftfreq_2", test_fftfreq_2), & - new_unittest("fftfreq_3", test_fftfreq_3), & - new_unittest("rfftfreq", test_rfftfreq) & - ] - - end subroutine collect_utils - - subroutine test_fftshift_complex(error) - type(error_type), allocatable, intent(out) :: error - complex(kind=rk) :: xeven(4) = [1, 2, 3, 4] - complex(kind=rk) :: xodd(5) = [1, 2, 3, 4, 5] - - call check(error, all(fftshift(xeven) == [complex(kind=rk) :: 3, 4, 1, 2]), & - "all(fftshift(xeven) == [complex(kind=rk) :: 3, 4, 1, 2]) failed.") - if (allocated(error)) return - call check(error, all(fftshift(xodd) == [complex(kind=rk) :: 4, 5, 1, 2, 3]), & - "all(fftshift(xodd) == [complex(kind=rk) :: 4, 5, 1, 2, 3]) failed.") - - end subroutine test_fftshift_complex - - subroutine test_fftshift_real(error) - type(error_type), allocatable, intent(out) :: error - real(kind=rk) :: xeven(4) = [1, 2, 3, 4] - real(kind=rk) :: xodd(5) = [1, 2, 3, 4, 5] - - call check(error, all(fftshift(xeven) == [real(kind=rk) :: 3, 4, 1, 2]), & - "all(fftshift(xeven) == [real(kind=rk) :: 3, 4, 1, 2]) failed.") - if (allocated(error)) return - call check(error, all(fftshift(xodd) == [real(kind=rk) :: 4, 5, 1, 2, 3]), & - "all(fftshift(xodd) == [real(kind=rk) :: 4, 5, 1, 2, 3]) failed.") - - end subroutine test_fftshift_real - - subroutine test_ifftshift_complex(error) - type(error_type), allocatable, intent(out) :: error - integer :: i - - complex(kind=rk) :: xeven(4) = [3, 4, 1, 2] - complex(kind=rk) :: xodd(5) = [4, 5, 1, 2, 3] - - call check(error, all(ifftshift(xeven) == [complex(kind=rk) ::(i, i=1, 4)]), & - "all(ifftshift(xeven) == [complex(kind=rk) ::(i, i=1, 4)]) failed.") - if (allocated(error)) return - call check(error, all(ifftshift(xodd) == [complex(kind=rk) ::(i, i=1, 5)]), & - "all(ifftshift(xodd) == [complex(kind=rk) ::(i, i=1, 5)]) failed.") - - end subroutine test_ifftshift_complex - - subroutine test_ifftshift_real(error) - type(error_type), allocatable, intent(out) :: error - integer :: i - - real(kind=rk) :: xeven(4) = [3, 4, 1, 2] - real(kind=rk) :: xodd(5) = [4, 5, 1, 2, 3] - - call check(error, all(ifftshift(xeven) == [real(kind=rk) ::(i, i=1, 4)]), & - "all(ifftshift(xeven) == [real(kind=rk) ::(i, i=1, 4)]) failed.") - if (allocated(error)) return - call check(error, all(ifftshift(xodd) == [real(kind=rk) ::(i, i=1, 5)]), & - "all(ifftshift(xodd) == [real(kind=rk) ::(i, i=1, 5)]) failed.") - - end subroutine test_ifftshift_real - - subroutine test_fftfreq_1(error) - type(error_type), allocatable, intent(out) :: error - integer, dimension(8) :: target1 = [0, 1, 2, 3, -4, -3, -2, -1] - integer, dimension(9) :: target2 = [0, 1, 2, 3, 4, -4, -3, -2, -1] - - call check(error, all(fftfreq(8) == target1),& - "all(fftfreq(8) == target1) failed.") - if (allocated(error)) return - call check(error, all(fftfreq(9) == target2),& - "all(fftfreq(9) == target2) failed.") - end subroutine test_fftfreq_1 - - subroutine test_fftfreq_2(error) - implicit none - type(error_type), allocatable, intent(out) :: error - - real(rk), parameter :: tol = 1.0e-12_rk - real(rk), parameter :: twopi = 8*atan(1.0_rk) ! 2*pi - complex(rk), parameter :: imu = (0,1) ! imaginary unit - - integer, parameter :: n = 128 - integer :: i - complex(rk), dimension(n) :: xvec, xfou - real(rk), dimension(n) :: xtrue - - do i = 1, n - xvec(i) = cos(twopi*(i-1)/n) - xtrue(i) = -sin(twopi*(i-1)/n) ! derivative in physical space - end do - - xfou = fft(xvec)/n - xfou = imu*fftfreq(n)*xfou ! derivative in Fourier space - xvec = ifft(xfou) - call check(error, maxval(abs(xvec-xtrue)) < tol, & - "maxval(abs(xvec-xtrue)) < tol failed.") - end subroutine test_fftfreq_2 - - subroutine test_fftfreq_3(error) - implicit none - type(error_type), allocatable, intent(out) :: error - - real(rk), parameter :: tol = 1.0e-12_rk - real(rk), parameter :: twopi = 8*atan(1.0_rk) ! 2*pi - complex(rk), parameter :: imu = (0,1) ! imaginary unit - - integer, parameter :: n = 135 - integer :: i - complex(rk), dimension(n) :: xvec, xfou - real(rk), dimension(n) :: xtrue - - do i = 1, n - xvec(i) = cos(twopi*(i-1)/n) - xtrue(i) = -sin(twopi*(i-1)/n) ! derivative in physical space - end do - - xfou = fft(xvec)/n - xfou = imu*fftfreq(n)*xfou ! derivative in Fourier space - xvec = ifft(xfou) - call check(error, maxval(abs(xvec-xtrue)) < tol, & - "maxval(abs(xvec-xtrue)) < tol failed.") - end subroutine test_fftfreq_3 - - subroutine test_rfftfreq(error) - type(error_type), allocatable, intent(out) :: error - integer, dimension(8) :: target1 = [0, 1, 1, 2, 2, 3, 3, -4] - integer, dimension(9) :: target2 = [0, 1, 1, 2, 2, 3, 3, 4, 4] - - call check(error, all(rfftfreq(8) == target1),& - "all(rfftfreq(8) == target1) failed.") - if (allocated(error)) return - call check(error, all(rfftfreq(9) == target2),& - "all(rfftfreq(9) == target2) failed.") - end subroutine test_rfftfreq + !> Collect all exported unit tests + subroutine collect_utils(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("fftshift_complex", test_fftshift_complex), & + new_unittest("fftshift_real", test_fftshift_real), & + new_unittest("ifftshift_complex", test_fftshift_complex), & + new_unittest("ifftshift_real", test_fftshift_real), & + new_unittest("fftfreq_1", test_fftfreq_1), & + new_unittest("fftfreq_2", test_fftfreq_2), & + new_unittest("fftfreq_3", test_fftfreq_3), & + new_unittest("rfftfreq", test_rfftfreq) & + ] + + end subroutine collect_utils + + subroutine test_fftshift_complex(error) + type(error_type), allocatable, intent(out) :: error + complex(kind=rk) :: xeven(4) = [1, 2, 3, 4] + complex(kind=rk) :: xodd(5) = [1, 2, 3, 4, 5] + + call check(error, all(fftshift(xeven) == [complex(kind=rk) :: 3, 4, 1, 2]), & + "all(fftshift(xeven) == [complex(kind=rk) :: 3, 4, 1, 2]) failed.") + if (allocated(error)) return + call check(error, all(fftshift(xodd) == [complex(kind=rk) :: 4, 5, 1, 2, 3]), & + "all(fftshift(xodd) == [complex(kind=rk) :: 4, 5, 1, 2, 3]) failed.") + + end subroutine test_fftshift_complex + + subroutine test_fftshift_real(error) + type(error_type), allocatable, intent(out) :: error + real(kind=rk) :: xeven(4) = [1, 2, 3, 4] + real(kind=rk) :: xodd(5) = [1, 2, 3, 4, 5] + + call check(error, all(fftshift(xeven) == [real(kind=rk) :: 3, 4, 1, 2]), & + "all(fftshift(xeven) == [real(kind=rk) :: 3, 4, 1, 2]) failed.") + if (allocated(error)) return + call check(error, all(fftshift(xodd) == [real(kind=rk) :: 4, 5, 1, 2, 3]), & + "all(fftshift(xodd) == [real(kind=rk) :: 4, 5, 1, 2, 3]) failed.") + + end subroutine test_fftshift_real + + subroutine test_ifftshift_complex(error) + type(error_type), allocatable, intent(out) :: error + integer :: i + + complex(kind=rk) :: xeven(4) = [3, 4, 1, 2] + complex(kind=rk) :: xodd(5) = [4, 5, 1, 2, 3] + + call check(error, all(ifftshift(xeven) == [complex(kind=rk) ::(i, i=1, 4)]), & + "all(ifftshift(xeven) == [complex(kind=rk) ::(i, i=1, 4)]) failed.") + if (allocated(error)) return + call check(error, all(ifftshift(xodd) == [complex(kind=rk) ::(i, i=1, 5)]), & + "all(ifftshift(xodd) == [complex(kind=rk) ::(i, i=1, 5)]) failed.") + + end subroutine test_ifftshift_complex + + subroutine test_ifftshift_real(error) + type(error_type), allocatable, intent(out) :: error + integer :: i + + real(kind=rk) :: xeven(4) = [3, 4, 1, 2] + real(kind=rk) :: xodd(5) = [4, 5, 1, 2, 3] + + call check(error, all(ifftshift(xeven) == [real(kind=rk) ::(i, i=1, 4)]), & + "all(ifftshift(xeven) == [real(kind=rk) ::(i, i=1, 4)]) failed.") + if (allocated(error)) return + call check(error, all(ifftshift(xodd) == [real(kind=rk) ::(i, i=1, 5)]), & + "all(ifftshift(xodd) == [real(kind=rk) ::(i, i=1, 5)]) failed.") + + end subroutine test_ifftshift_real + + subroutine test_fftfreq_1(error) + type(error_type), allocatable, intent(out) :: error + integer, dimension(8) :: target1 = [0, 1, 2, 3, -4, -3, -2, -1] + integer, dimension(9) :: target2 = [0, 1, 2, 3, 4, -4, -3, -2, -1] + + call check(error, all(fftfreq(8) == target1), & + "all(fftfreq(8) == target1) failed.") + if (allocated(error)) return + call check(error, all(fftfreq(9) == target2), & + "all(fftfreq(9) == target2) failed.") + end subroutine test_fftfreq_1 + + subroutine test_fftfreq_2(error) + implicit none + type(error_type), allocatable, intent(out) :: error + + real(rk), parameter :: tol = 1.0e-12_rk + real(rk), parameter :: twopi = 8*atan(1.0_rk) ! 2*pi + complex(rk), parameter :: imu = (0, 1) ! imaginary unit + + integer, parameter :: n = 128 + integer :: i + complex(rk), dimension(n) :: xvec, xfou + real(rk), dimension(n) :: xtrue + + do i = 1, n + xvec(i) = cos(twopi*(i - 1)/n) + xtrue(i) = -sin(twopi*(i - 1)/n) ! derivative in physical space + end do + + xfou = fft(xvec)/n + xfou = imu*fftfreq(n)*xfou ! derivative in Fourier space + xvec = ifft(xfou) + call check(error, maxval(abs(xvec - xtrue)) < tol, & + "maxval(abs(xvec-xtrue)) < tol failed.") + end subroutine test_fftfreq_2 + + subroutine test_fftfreq_3(error) + implicit none + type(error_type), allocatable, intent(out) :: error + + real(rk), parameter :: tol = 1.0e-12_rk + real(rk), parameter :: twopi = 8*atan(1.0_rk) ! 2*pi + complex(rk), parameter :: imu = (0, 1) ! imaginary unit + + integer, parameter :: n = 135 + integer :: i + complex(rk), dimension(n) :: xvec, xfou + real(rk), dimension(n) :: xtrue + + do i = 1, n + xvec(i) = cos(twopi*(i - 1)/n) + xtrue(i) = -sin(twopi*(i - 1)/n) ! derivative in physical space + end do + + xfou = fft(xvec)/n + xfou = imu*fftfreq(n)*xfou ! derivative in Fourier space + xvec = ifft(xfou) + call check(error, maxval(abs(xvec - xtrue)) < tol, & + "maxval(abs(xvec-xtrue)) < tol failed.") + end subroutine test_fftfreq_3 + + subroutine test_rfftfreq(error) + type(error_type), allocatable, intent(out) :: error + integer, dimension(8) :: target1 = [0, 1, 1, 2, 2, 3, 3, -4] + integer, dimension(9) :: target2 = [0, 1, 1, 2, 2, 3, 3, 4, 4] + + call check(error, all(rfftfreq(8) == target1), & + "all(rfftfreq(8) == target1) failed.") + if (allocated(error)) return + call check(error, all(rfftfreq(9) == target2), & + "all(rfftfreq(9) == target2) failed.") + end subroutine test_rfftfreq end module test_fftpack_utils + +program test_utils + use, intrinsic :: iso_fortran_env, only: error_unit + use testdrive, only: run_testsuite, new_testsuite, testsuite_type + use test_fftpack_utils, only: collect_utils + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("utils", collect_utils) & + ] + + do is = 1, size(testsuites) + write (error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write (error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if +end program