diff --git a/config/meson.build b/config/meson.build new file mode 100644 index 00000000..7a3ca04d --- /dev/null +++ b/config/meson.build @@ -0,0 +1,18 @@ +os = host_machine.system() +fc = meson.get_compiler('fortran') +cc = fc +# cc = meson.get_compiler('c') +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', + ) +endif diff --git a/docs/download_and_install.md b/docs/download_and_install.md index bd279586..bea6aef5 100644 --- a/docs/download_and_install.md +++ b/docs/download_and_install.md @@ -15,36 +15,60 @@ setup.py test ```
-## Source code -**Download the ddX source code** at: +## Source Code + +**Download the ddX source code** from GitHub: + ``` -git@github.com:ddsolvation/ddX.git +git clone git@github.com:ddsolvation/ddX.git ``` -**Download and install** ddX as follows: + +Then change into the cloned directory: + ``` -> git clone git@github.com:ddsolvation/ddX.git -> cd ddX -> mkdir build -> cd build -> cmake .. -> make +cd ddX ``` -Per default, the library is located in /src. + +The main Fortran sources for ddX reside in the `src/` folder. You can build ddX using either **CMake** or **Meson**, as outlined below. + +--- + +## Building with CMake + +1. Create a build directory and enter it: + ```bash + mkdir build + cd build + ``` +2. Run CMake to configure: + ```bash + cmake .. + ``` +3. Compile the library and executables: + ```bash + make + ``` +4. (Optional) Run the test suite: + ```bash + make test + ``` + +By default, the compiled library and executables will appear in the `build` folder. The original sources remain in `src/`. **Build the documentation** as follows (after you have done the above process): -``` -> cd build -> make docs +```bash +cd build +make docs ``` **To see the documentation** -``` -> cd ../doxygen -> pwd +```bash +cd ../doxygen +pwd ``` Copy the link shown by pwd and add /index.html in a web browser -#### Hints and hacks -1. For specifying compilers use + +**Specifying compilers** can be done by passing the desired compilers to CMake: ``` cmake -D CMAKE_CXX_COMPILER=/usr/local/bin/g++-11 CMAKE_Fortran_COMPILER=/usr/local/bin/gfortran-11 .. ``` @@ -53,3 +77,28 @@ or cmake -D CMAKE_CXX_COMPILER=icx CMAKE_Fortran_COMPILER=ifort .. ``` **NOTE**: Replace with the compilers you desire. + + +--- + +## Building with Meson + +[meson](https://mesonbuild.com) version 0.61 or newer, with + a build-system backend, *i.e.* [ninja](https://ninja-build.org) version 1.10 or newer, are required. + +1. Set up a build directory: + ```bash + meson setup build + ``` +2. Compile: + ```bash + meson compile -C build + ``` +3. Run the test suite (and print any error logs): + ```bash + meson test -C build + ``` + +Again, the built library and any executables will appear in the `build` folder, while the ddX source remains in `src/`. + + diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..f8926aca --- /dev/null +++ b/meson.build @@ -0,0 +1,130 @@ +project('ddX', + 'fortran', + 'cpp', + version: '0.6.0', + license: 'LGPL-3.0-or-later', + meson_version: '>=0.57.2', + default_options: [ + 'default_library=both', + ], +) + +install = not (meson.is_subproject() and get_option('default_library') == 'static') +fc = meson.get_compiler('fortran') +fc_id = fc.get_id() +fortran_compiler = fc + +lib_deps = [] +if get_option('openmp') + omp_dep = dependency('openmp') + lib_deps += omp_dep +endif + +lapack_vendor = get_option('lapack') +if lapack_vendor == 'auto' + if fc_id == 'intel' + lapack_vendor = 'mkl' + endif +endif + +if lapack_vendor == 'mkl' + mkl_dep = [] + if fc_id == 'intel' + mkl_dep += fc.find_library('mkl_intel_lp64') + if get_option('openmp') + mkl_dep += fc.find_library('mkl_intel_thread') + endif + elif fc_id == 'gcc' + mkl_dep += fc.find_library('mkl_gf_lp64') + if get_option('openmp') + mkl_dep += fc.find_library('mkl_gnu_thread') + endif + else + error('MKL not supported for this compiler') + endif + if not get_option('openmp') + mkl_dep += fc.find_library('mkl_tbb_thread') + endif + mkl_dep += fc.find_library('mkl_core') + lib_deps += mkl_dep + add_project_arguments(['-DWITH_MKL'], language: 'fortran') +elif lapack_vendor == 'mkl-rt' + mkl_dep = fc.find_library('mkl_rt') + lib_deps += mkl_dep + add_project_arguments(['-DWITH_MKL'], language: 'fortran') +elif lapack_vendor == 'openblas' + openblas_dep = dependency('openblas', required: false) + if not openblas_dep.found() + openblas_dep = fc.find_library('openblas') + endif + lib_deps += openblas_dep + if not fc.links('external dsytrs; call dsytrs(); end', dependencies: openblas_dep) + lapack_dep = dependency('lapack', required: false) + if not lapack_dep.found() + lapack_dep = fc.find_library('lapack') + endif + lib_deps += lapack_dep + endif + +elif lapack_vendor == 'custom' + foreach lib: get_option('custom_libraries') + lib_deps += fc.find_library(lib) + endforeach + +else + lapack_dep = dependency('lapack', required: false) + if not lapack_dep.found() + lapack_dep = fc.find_library('lapack') + endif + lib_deps += lapack_dep + blas_dep = dependency('blas', required: false) + if not blas_dep.found() + blas_dep = fc.find_library('blas') + endif + lib_deps += blas_dep +endif + +subdir('config') + +ddx_library = library( + 'ddX', + sources: [ + 'src/ddx.f90', + 'src/cbessel.f90', + 'src/ddx_driver.f90', + 'src/ddx_lpb_core.f90', + 'src/ddx_workspace.f90', + 'src/ddx_errors.f90', + 'src/ddx_lpb.f90', + 'src/llgnew.f', + 'src/ddx_cinterface.f90', + 'src/ddx_multipolar_solutes.f90', + 'src/ddx_constants.f90', + 'src/ddx_gradients.f90', + 'src/ddx_operators.f90', + 'src/ddx_core.f90', + 'src/ddx_parameters.f90', + 'src/ddx_cosmo.f90', + 'src/ddx_harmonics.f90', + 'src/ddx_pcm.f90', + 'src/ddx_definitions.f90', + 'src/ddx_legacy.f90', + 'src/ddx_solvers.f90' + ], + dependencies: lib_deps +) + +ddx_dep = declare_dependency( + link_with: ddx_library, + dependencies: lib_deps +) + +executable_target = executable( + 'ddX_exec', + sources: ['src/ddx_driver.f90'], + link_with: [ddx_library], + dependencies: lib_deps +) + +subdir('tests') +subdir('tests/standalone_tests') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..52253026 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,24 @@ +option( + 'lapack', + type: 'combo', + value: 'auto', + yield: true, + choices: ['auto', 'mkl', 'mkl-rt', 'openblas', 'netlib', 'custom'], + description : 'linear algebra backend', +) + +option( + 'custom_libraries', + type: 'array', + value: [], + yield: true, + description: 'libraries to load for custom linear algebra backend', +) + +option( + 'openmp', + type: 'boolean', + value: true, + yield: true, + description: 'use OpenMP parallelisation', +) \ No newline at end of file diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 00000000..6be98ae4 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,57 @@ +project_dir = meson.current_source_dir() + +# Test sources with their arguments +test_sources = [ + ['ddx_core.f90', []], + ['ddx_operators.f90', []], + ['bessel.f90', []], + ['force.f90', [join_paths(project_dir, 'Input_force.txt')]], + ['ddx_driver.f90', [ + join_paths(project_dir, 'data/ddpcm_force_fmm.in') + ' ' + join_paths(project_dir, 'data/ddpcm_force_fmm.out') + ' 1E-12', + join_paths(project_dir, 'data/ddcosmo_force_fmm.in') + ' ' + join_paths(project_dir, 'data/ddcosmo_force_fmm.out') + ' 1E-12' + ]], + ['force_ddlpb.f90', [join_paths(project_dir, 'data/ddlpb_force.txt')]], + ['ddlpb_esolv.f90', [join_paths(project_dir, 'data/ddlpb_force.txt')]], + ['matrix_derivatives.f90', [join_paths(project_dir, 'data/ddlpb_force.txt')]], + ['matrix_adjoint.f90', [join_paths(project_dir, 'data/ddlpb_force.txt')]], + ['matrix_solvers.f90', [join_paths(project_dir, 'data/ddlpb_force.txt')]], + ['m2l.f90', []], + ['multipolar_solutes.f90', []], + ['error.f90', []] +] + +# Create test executables and add tests +foreach source_entry : test_sources + src = source_entry[0] + args = source_entry[1] + executable_name = src.split('.')[0] + + test_executable = executable( + executable_name, + src, + dependencies: [lib_deps, ddx_dep], # Link to ddx_dep + install: false + ) + + # If the executable is ddx_driver, store it for the extra test + if executable_name == 'ddx_driver' + ddx_driver_exe = test_executable + endif + + if args.length() == 0 + test(executable_name, test_executable) + else + idx = 0 + foreach arg_set : args + test( + executable_name + '_' + idx.to_string(), + test_executable, + args: arg_set.split(), + timeout: 240 + ) + message('test ' + idx.to_string() + ' ' + executable_name + ' ' + arg_set) + idx += 1 + endforeach + endif +endforeach + diff --git a/tests/standalone_tests/meson.build b/tests/standalone_tests/meson.build new file mode 100644 index 00000000..33e2d0e8 --- /dev/null +++ b/tests/standalone_tests/meson.build @@ -0,0 +1,50 @@ +# Copy run_test.py to the build directory +# (If run_test.py already has +x permission, this copy should preserve it on most OS.) +configure_file( + input: 'run_test.py', + output: 'run_test.py', + copy: true +) + +# Define a list of test basenames. +standalone_tests = [ + 'cosmo', + 'cosmo_fmm', + 'cosmo_incore', + 'pcm', + 'pcm_fmm', + 'pcm_incore', + 'lpb', + 'lpb_fmm', + 'lpb_incore' +] + +foreach t : standalone_tests + # Copy each test’s .txt and .ref files into the build dir. + configure_file( + input: t + '.txt', + output: t + '.txt', + copy: true + ) + configure_file( + input: t + '.ref', + output: t + '.ref', + copy: true + ) + + # Generate a small shell wrapper from meson_test_wrapper.in + # We replace the placeholders with the actual test name and build directory. + meson_test_wrapper = configure_file( + input: 'meson_test_wrapper.in', + output: t + '_wrapper.sh', + configuration: { + 'BUILD_DIR': meson.project_build_root(), + 'TESTNAME': t + }, + copy: false + ) + + # Finally, register the wrapper script as a test with Meson. + # This call has exactly two arguments: the test name, and the wrapper "File". + test(t, meson_test_wrapper) +endforeach diff --git a/tests/standalone_tests/meson_test_wrapper.in b/tests/standalone_tests/meson_test_wrapper.in new file mode 100755 index 00000000..075e72b5 --- /dev/null +++ b/tests/standalone_tests/meson_test_wrapper.in @@ -0,0 +1,7 @@ +#!/bin/sh +# We do NOT rely on run_test.py being executable. +# Instead, we call it via python3, which is typically in PATH. + +cd @BUILD_DIR@/tests/standalone_tests + +./run_test.py @TESTNAME@ \ No newline at end of file