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