Scalapack uses a library stack that can be challenging to manage. Not least of all since many Scalapacks ship with broken / incomplete pkg-config files and CMake FindScalapack.cmake This resolves those issues for typical Scalapack setups including: * Linux: Intel MKL or OpenMPI + Netlib * MacOS: Intel MKL or OpenMPI + Netlib * Windows: Intel MKL (OpenMPI not available on Windows)pull/6228/head
parent
818c92003c
commit
92b77cb321
8 changed files with 314 additions and 3 deletions
@ -0,0 +1,67 @@ |
|||||||
|
name: ci_frameworks |
||||||
|
|
||||||
|
on: |
||||||
|
push: |
||||||
|
paths: |
||||||
|
- "mesonbuild/dependencies/**" |
||||||
|
- "test cases/frameworks/**" |
||||||
|
- ".github/workflows/frameworks.yml" |
||||||
|
pull_request: |
||||||
|
paths: |
||||||
|
- "mesonbuild/dependencies/**" |
||||||
|
- "test cases/frameworks/**" |
||||||
|
- ".github/workflows/frameworks.yml" |
||||||
|
|
||||||
|
jobs: |
||||||
|
|
||||||
|
scalapack_linux: |
||||||
|
runs-on: ubuntu-latest |
||||||
|
steps: |
||||||
|
- uses: actions/checkout@v1 |
||||||
|
- uses: actions/setup-python@v1 |
||||||
|
with: |
||||||
|
python-version: '3.x' |
||||||
|
- run: python -m pip install . |
||||||
|
- name: install prereq |
||||||
|
run: | |
||||||
|
sudo apt update -yq |
||||||
|
sudo apt install -yq --no-install-recommends pkg-config ninja-build gfortran liblapack-dev libscalapack-mpi-dev libopenmpi-dev openmpi-bin |
||||||
|
- run: meson setup "test cases/frameworks/30 scalapack" build |
||||||
|
env: |
||||||
|
FC: gfortran |
||||||
|
CC: gcc |
||||||
|
- run: ninja -C build |
||||||
|
- uses: actions/upload-artifact@v1 |
||||||
|
if: failure() |
||||||
|
with: |
||||||
|
name: Scalpack_Linux_Build |
||||||
|
path: build/meson-logs/meson-log.txt |
||||||
|
- run: meson test -C build -v |
||||||
|
- uses: actions/upload-artifact@v1 |
||||||
|
if: failure() |
||||||
|
with: |
||||||
|
name: Scalapack_Linux_Test |
||||||
|
path: build/meson-logs/testlog.txt |
||||||
|
|
||||||
|
scalapack_mac: |
||||||
|
runs-on: macos-latest |
||||||
|
steps: |
||||||
|
- uses: actions/checkout@v1 |
||||||
|
- uses: actions/setup-python@v1 |
||||||
|
with: |
||||||
|
python-version: '3.x' |
||||||
|
- run: python -m pip install -e . |
||||||
|
- run: brew install pkg-config ninja gcc openmpi lapack scalapack |
||||||
|
- run: meson setup "test cases/frameworks/30 scalapack" build |
||||||
|
- run: ninja -C build |
||||||
|
- uses: actions/upload-artifact@v1 |
||||||
|
if: failure() |
||||||
|
with: |
||||||
|
name: Scalapack_Mac_build |
||||||
|
path: build/meson-logs/meson-log.txt |
||||||
|
- run: meson test -C build -v |
||||||
|
- uses: actions/upload-artifact@v1 |
||||||
|
if: failure() |
||||||
|
with: |
||||||
|
name: Scalapack_Mac_test |
||||||
|
path: build/meson-logs/testlog.txt |
@ -0,0 +1,14 @@ |
|||||||
|
## Scalapack |
||||||
|
|
||||||
|
added in **0.53.0**: |
||||||
|
|
||||||
|
```meson |
||||||
|
scalapack = dependency('scalapack') |
||||||
|
``` |
||||||
|
|
||||||
|
Historically and through today, typical Scalapack setups have broken and incomplete pkg-config or |
||||||
|
FindScalapack.cmake. Meson handles finding Scalapack on setups including: |
||||||
|
|
||||||
|
* Linux: Intel MKL or OpenMPI + Netlib |
||||||
|
* MacOS: Intel MKL or OpenMPI + Netlib |
||||||
|
* Windows: Intel MKL (OpenMPI not available on Windows) |
@ -0,0 +1,121 @@ |
|||||||
|
# Copyright 2013-2019 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from pathlib import Path |
||||||
|
import os |
||||||
|
|
||||||
|
from .. import mesonlib |
||||||
|
from .base import (CMakeDependency, ExternalDependency, PkgConfigDependency) |
||||||
|
|
||||||
|
|
||||||
|
class ScalapackDependency(ExternalDependency): |
||||||
|
def __init__(self, environment, kwargs: dict): |
||||||
|
language = kwargs.get('language') |
||||||
|
super().__init__('scalapack', environment, language, kwargs) |
||||||
|
kwargs['required'] = False |
||||||
|
kwargs['silent'] = True |
||||||
|
self.is_found = False |
||||||
|
self.static = kwargs.get('static', False) |
||||||
|
|
||||||
|
# 1. try pkg-config |
||||||
|
pkgconfig_files = [] |
||||||
|
mklroot = None |
||||||
|
is_gcc = None |
||||||
|
if language == 'fortran': |
||||||
|
is_gcc = environment.detect_fortran_compiler(self.for_machine).get_id() == 'gcc' |
||||||
|
elif language == 'c': |
||||||
|
is_gcc = environment.detect_c_compiler(self.for_machine).get_id() == 'gcc' |
||||||
|
elif language == 'cpp': |
||||||
|
is_gcc = environment.detect_cpp_compiler(self.for_machine).get_id() == 'gcc' |
||||||
|
# Intel MKL works with non-Intel compilers too -- but not gcc on windows |
||||||
|
if 'MKLROOT' in os.environ and not (mesonlib.is_windows() and is_gcc): |
||||||
|
try: |
||||||
|
mklroot = Path(os.environ['MKLROOT']).resolve() |
||||||
|
except Exception: |
||||||
|
pass |
||||||
|
if mklroot is not None: |
||||||
|
# MKL pkg-config is a start, but you have to add / change stuff |
||||||
|
# https://software.intel.com/en-us/articles/intel-math-kernel-library-intel-mkl-and-pkg-config-tool |
||||||
|
pkgconfig_files = ( |
||||||
|
['mkl-static-lp64-iomp'] if self.static else ['mkl-dynamic-lp64-iomp'] |
||||||
|
) |
||||||
|
if mesonlib.is_windows(): |
||||||
|
suffix = '.lib' |
||||||
|
elif self.static: |
||||||
|
suffix = '.a' |
||||||
|
else: |
||||||
|
suffix = '' |
||||||
|
libdir = mklroot / 'lib/intel64' |
||||||
|
# Intel compiler might not have Parallel Suite |
||||||
|
pkgconfig_files += ['scalapack-openmpi', 'scalapack'] |
||||||
|
|
||||||
|
for pkg in pkgconfig_files: |
||||||
|
pkgdep = PkgConfigDependency( |
||||||
|
pkg, environment, kwargs, language=self.language |
||||||
|
) |
||||||
|
if pkgdep.found(): |
||||||
|
self.compile_args = pkgdep.get_compile_args() |
||||||
|
if mklroot: |
||||||
|
link_args = pkgdep.get_link_args() |
||||||
|
if is_gcc: |
||||||
|
for i, a in enumerate(link_args): |
||||||
|
if 'mkl_intel_lp64' in a: |
||||||
|
link_args[i] = a.replace('intel', 'gf') |
||||||
|
break |
||||||
|
# MKL pkg-config omits scalapack |
||||||
|
# be sure "-L" and "-Wl" are first if present |
||||||
|
i = 0 |
||||||
|
for j, a in enumerate(link_args): |
||||||
|
if a.startswith(('-L', '-Wl')): |
||||||
|
i = j + 1 |
||||||
|
elif j > 3: |
||||||
|
break |
||||||
|
if mesonlib.is_windows() or self.static: |
||||||
|
link_args.insert( |
||||||
|
i, str(libdir / ('mkl_scalapack_lp64' + suffix)) |
||||||
|
) |
||||||
|
link_args.insert( |
||||||
|
i + 1, str(libdir / ('mkl_blacs_intelmpi_lp64' + suffix)) |
||||||
|
) |
||||||
|
else: |
||||||
|
link_args.insert(i, '-lmkl_scalapack_lp64') |
||||||
|
link_args.insert(i + 1, '-lmkl_blacs_intelmpi_lp64') |
||||||
|
else: |
||||||
|
link_args = pkgdep.get_link_args() |
||||||
|
self.link_args = link_args |
||||||
|
|
||||||
|
self.version = pkgdep.get_version() |
||||||
|
if self.version == 'unknown' and mklroot: |
||||||
|
try: |
||||||
|
v = ( |
||||||
|
mklroot.as_posix() |
||||||
|
.split('compilers_and_libraries_')[1] |
||||||
|
.split('/', 1)[0] |
||||||
|
) |
||||||
|
if v: |
||||||
|
self.version = v |
||||||
|
except IndexError: |
||||||
|
pass |
||||||
|
|
||||||
|
self.is_found = True |
||||||
|
self.pcdep = pkgdep |
||||||
|
return |
||||||
|
# 2. try CMake |
||||||
|
cmakedep = CMakeDependency('Scalapack', environment, kwargs) |
||||||
|
if cmakedep.found(): |
||||||
|
self.compile_args = cmakedep.get_compile_args() |
||||||
|
self.link_args = cmakedep.get_link_args() |
||||||
|
self.version = cmakedep.get_version() |
||||||
|
self.is_found = True |
||||||
|
return |
@ -0,0 +1,34 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
// #include <mkl.h>
|
||||||
|
// #include <mkl_scalapack.h>
|
||||||
|
// #include <mkl_blacs.h>
|
||||||
|
|
||||||
|
extern float pslamch_(const int *, const char *); |
||||||
|
extern void blacs_pinfo_(int *, int *); |
||||||
|
extern void blacs_get_(const int *, const int *, int *); |
||||||
|
extern void blacs_gridinit_(int *, const char *, const int *, const int *); |
||||||
|
extern void blacs_gridinfo_(const int *, int *, int *, int *, int *); |
||||||
|
extern void blacs_gridexit_(const int *); |
||||||
|
extern void blacs_exit_(const int *); |
||||||
|
|
||||||
|
int main(void){ |
||||||
|
|
||||||
|
int myid, nprocs, ictxt, mycol, myrow, npcol=2, nprow=2; |
||||||
|
const int i0=0, i1=1, in1=-1; |
||||||
|
|
||||||
|
blacs_pinfo_(&myid, &nprocs); |
||||||
|
blacs_get_(&in1, &i0, &ictxt); |
||||||
|
blacs_gridinit_(&ictxt, "C", &nprocs, &i1); |
||||||
|
|
||||||
|
blacs_gridinfo_(&ictxt, &nprow, &npcol, &myrow, &mycol); |
||||||
|
|
||||||
|
float eps = pslamch_(&ictxt, "E"); |
||||||
|
|
||||||
|
if (myrow == mycol) printf("OK: Scalapack C: eps= %f\n", eps); |
||||||
|
|
||||||
|
blacs_gridexit_(&ictxt); |
||||||
|
blacs_exit_(&i0); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
! minimal Scalapack demo |
||||||
|
implicit none |
||||||
|
|
||||||
|
integer :: ictxt, myid, nprocs, mycol, myrow, npcol, nprow |
||||||
|
real :: eps |
||||||
|
real, external :: pslamch |
||||||
|
|
||||||
|
! arbitrary test parameters |
||||||
|
npcol = 2 |
||||||
|
nprow = 2 |
||||||
|
|
||||||
|
call blacs_pinfo(myid, nprocs) |
||||||
|
call blacs_get(-1, 0, ictxt) |
||||||
|
call blacs_gridinit(ictxt, "C", nprocs, 1) |
||||||
|
|
||||||
|
call blacs_gridinfo(ictxt, nprow, npcol, myrow, mycol) |
||||||
|
|
||||||
|
eps = pslamch(ictxt, 'E') |
||||||
|
|
||||||
|
if(myrow == mycol) print '(A, F10.6)', "OK: Scalapack Fortran eps=", eps |
||||||
|
|
||||||
|
call blacs_gridexit(ictxt) |
||||||
|
call blacs_exit(0) |
||||||
|
|
||||||
|
end program |
@ -0,0 +1,42 @@ |
|||||||
|
project('test scalapack', 'c') |
||||||
|
|
||||||
|
cc = meson.get_compiler('c') |
||||||
|
|
||||||
|
mpiexec = find_program('mpiexec', required: false) |
||||||
|
if not mpiexec.found() |
||||||
|
error('MESON_SKIP_TEST: mpiexec not found') |
||||||
|
endif |
||||||
|
|
||||||
|
mpi_c = dependency('mpi', language: 'c', required: false) |
||||||
|
if not mpi_c.found() |
||||||
|
error('MESON_SKIP_TEST: MPI library not available') |
||||||
|
endif |
||||||
|
|
||||||
|
# examples can run with any number of MPI images. |
||||||
|
mpi_args = ['-np', '1'] |
||||||
|
|
||||||
|
scalapack = dependency('scalapack', required: false) |
||||||
|
if not scalapack.found() |
||||||
|
error('MESON_SKIP_TEST: scalapack library not available') |
||||||
|
endif |
||||||
|
|
||||||
|
# https://software.intel.com/en-us/mkl-developer-reference-c-scalapack-routines |
||||||
|
c_exe = executable('scalapack_c', 'main.c', |
||||||
|
dependencies: [scalapack, mpi_c]) |
||||||
|
test('scalapack_c', mpiexec, |
||||||
|
args: [mpi_args, c_exe], |
||||||
|
timeout: 30) |
||||||
|
|
||||||
|
if add_languages('fortran') |
||||||
|
mpi_f = dependency('mpi', language: 'fortran', required: false) |
||||||
|
if not mpi_f.found() |
||||||
|
error('MESON_SKIP_TEST: MPI Fortran library not available') |
||||||
|
endif |
||||||
|
|
||||||
|
# https://software.intel.com/en-us/mkl-developer-reference-fortran-scalapack-routines |
||||||
|
f_exe = executable('scalapack_fortran', 'main.f90', |
||||||
|
dependencies: [scalapack, mpi_f]) |
||||||
|
test('scalapack_fortran', mpiexec, |
||||||
|
args: [mpi_args, f_exe], |
||||||
|
timeout: 30) |
||||||
|
endif |
Loading…
Reference in new issue