diff --git a/.github/workflows/test.macos.yml b/.github/workflows/test.macos.yml new file mode 100644 index 00000000..30c1be3b --- /dev/null +++ b/.github/workflows/test.macos.yml @@ -0,0 +1,37 @@ +name: Test MacOS + +on: + push: + branches: + - '**' + pull_request: + branches: [ main ] + +jobs: + + test: + name: Testing ${{matrix.os}} py-${{matrix.python-version}} + runs-on: ${{matrix.os}} + + strategy: + matrix: + os: ['macos-latest'] + python-version: ['3.11', '3.12'] + + steps: + - uses: actions/checkout@v2 + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v5 + + - run: which brew + - run: brew install gcc openblas lapack libomp + - run: ls /opt/homebrew/bin/ + - run: which gfortran-14 + + - run: FC=gfortran-14 make env_uv python_version=${{ matrix.python-version }} + + - run: make test + - run: make format + - run: FC=gfortran-14 make build + - run: make test-dist diff --git a/.github/workflows/test.ubuntu.yml b/.github/workflows/test.ubuntu.yml new file mode 100644 index 00000000..7c80df2e --- /dev/null +++ b/.github/workflows/test.ubuntu.yml @@ -0,0 +1,34 @@ +name: Test Ubuntu + +on: + push: + branches: + - '**' + pull_request: + branches: [ main ] + +jobs: + + test: + name: Testing ${{matrix.os}} py-${{matrix.python-version}} + runs-on: ${{matrix.os}} + + strategy: + matrix: + os: ['ubuntu-latest'] + python-version: ['3.11', '3.12'] + + steps: + - uses: actions/checkout@v2 + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v5 + + - run: sudo apt-get install -y gcc libomp-dev libopenblas-dev + + - run: make env_uv python_version=${{ matrix.python-version }} + + - run: make test + - run: make format + - run: make build + - run: make test-dist diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 19006dc5..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Test - -on: - push: - branches: - - '**' - pull_request: - branches: [ main ] - -jobs: - - test: - name: Testing - runs-on: "ubuntu-latest" - defaults: - run: - shell: bash -l {0} - strategy: - matrix: - python-version: ['3.12'] - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: dev - environment-file: ./environment_dev.yaml - python-version: ${{ matrix.python-version }} - - run: pip install . -v - - run: make test python=python - - run: make format python=python - - run: make build python=python - - run: make test-dist python=python diff --git a/Makefile b/Makefile index d9b7c650..f3673571 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ -python=./env/bin/python -mamba=mamba +env=env +python=./${env}/bin/python +python_version=3.12 +conda=mamba pkg=qmllib pip=./env/bin/pip pytest=pytest @@ -9,14 +11,25 @@ version_file=src/qmllib/version.py .PHONY: build -all: env +all: ${env} ## Setup env: - ${mamba} env create -f ./environment_dev.yaml -p ./env --quiet - ${python} -m pre_commit install + echo "TODO" + +env_uv: + which uv + uv venv ${env} --python ${python_version} + uv pip install -r requirements.txt --python ${python} + uv pip install -e . --python ${python} + make .git/hooks/pre-commit python=${python} + +env_conda: + which ${conda} + ${conda} env create -f ./environment.yaml -p ./${env} --quiet ${python} -m pip install -e . + make .git/hooks/pre-commit python=${python} ./.git/hooks/pre-commit: ${python} -m pre_commit install diff --git a/README.rst b/README.rst index 07cac449..a79057a1 100644 --- a/README.rst +++ b/README.rst @@ -28,14 +28,19 @@ and function naming. How to install ============== -You need a fortran compiler and math library. Default is `gfortran` and `openblas`. +You need a fortran compiler, OpenMP and a math library. Default is `gfortran` and `openblas`. +.. code-block:: bash + + sudo apt install gcc libomp-dev libopenblas-dev + +If you are on mac, you can install `gcc`, OpenML and BLAS/Lapack via `brew` .. code-block:: bash - sudo apt install libopenblas-dev gcc + brew install gcc libomp openblas lapack -You can install it via PyPi +You can then install via PyPi .. code-block:: bash diff --git a/_compile.py b/_compile.py index ee112da6..62381ac3 100644 --- a/_compile.py +++ b/_compile.py @@ -7,6 +7,8 @@ import numpy as np +DEFAULT_FC = "gfortran" + f90_modules = { "representations/frepresentations": ["frepresentations.f90"], "representations/facsf": ["facsf.f90"], @@ -38,18 +40,58 @@ def find_mkl(): def find_env() -> dict[str, str]: - """Find compiler flags""" + """Find compiler flag""" + + """ + For anaconda-like envs + TODO Find MKL + + For brew, + + brew install llvm libomp + brew install openblas lapack + + export LDFLAGS="-L/opt/homebrew/opt/lapack/lib" + export CPPFLAGS="-I/opt/homebrew/opt/lapack/include" + export LDFLAGS="-L/opt/homebrew/opt/libomp/lib" + export CPPFLAGS="-I/opt/homebrew/opt/libomp/include" + + """ + + fc = os.environ.get("FC", DEFAULT_FC) + + linker_mathlib_dirs = [ + "/usr/lib/", # Debian + "/lib/", # Alpine + "/usr/local/lib/", # FreeBSD + "/usr/lib64/", # Redhat + ] + + mathlib_path = None + + for dir in linker_mathlib_dirs: + + if not Path(dir).is_dir(): + continue + + mathlib_path = dir + break - # TODO Check if FCC is there, not not raise Error + if mathlib_path is None: + print("Unable to find mathlib path") + + # TODO Check if FC is there, not not raise Error # TODO Check if lapack / blas is there, if not raise Error + # TODO Check if omp is installed + + # TODO Find ifort flags, choose from FC + # TODO Find mkl lib - # TODO Find ifort flags, choose from FCC - # TODO Find math lib - # TODO Find os + # TODO Check if darwin, check for brew paths - COMPILER_FLAGS = [ + # Default GNU flags + compiler_flags = [ "-O3", - "-fopenmp", "-m64", "-march=native", "-fPIC", @@ -57,15 +99,47 @@ def find_env() -> dict[str, str]: "-Wno-unused-function", "-Wno-cpp", ] + compiler_openmp = [ + "-fopenmp", + ] + linker_flags = [ + "-lpthread", + "-lm", + "-ldl", + ] + linker_openmp = [ + "-lgomp", + ] + linker_math = [ + "-lblas", + "-llapack", + ] + + if mathlib_path is not None: + linker_math += [f"-L{mathlib_path}"] + + if sys.platform == "darwin": + + expected_omp_dir = Path("/opt/homebrew/opt/libomp/lib") + + if expected_omp_dir.is_dir(): + compiler_openmp = [ + "-fopenmp", + ] + linker_openmp = [ + f"-L{expected_omp_dir}", + "-lomp", + ] - extra_flags = ["-lgomp", "-lpthread", "-lm", "-ldl"] - math_flags = ["-L/usr/lib/", "-lblas", "-llapack"] + else: + print(f"Expected OpenMP dir not found: {expected_omp_dir}, compiling without OpenMP") + compiler_openmp = [] + linker_openmp = [] - fflags = [] + COMPILER_FLAGS - ldflags = [] + extra_flags + math_flags - fcc = "gfortran" + fflags = [] + compiler_flags + compiler_openmp + ldflags = [] + linker_flags + linker_math + linker_openmp - env = {"FFLAGS": " ".join(fflags), "LDFLAGS": " ".join(ldflags), "FCC": fcc} + env = {"FFLAGS": " ".join(fflags), "LDFLAGS": " ".join(ldflags), "FC": fc} return env @@ -73,6 +147,9 @@ def find_env() -> dict[str, str]: def main(): """Compile f90 in src/qmllib""" + print( + f"Using python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + ) print(f"Using numpy {np.__version__}") # Find and set Fortran compiler, compiler flags and linker flags diff --git a/environment.yaml b/environment.yaml new file mode 100644 index 00000000..48352dc7 --- /dev/null +++ b/environment.yaml @@ -0,0 +1,9 @@ +name: qmllib_dev +channels: + - conda-forge + - defaults +dependencies: + - python==3.12 + - pip + - pip: + - -r ./requirements.txt diff --git a/environment_dev.yaml b/environment_dev.yaml deleted file mode 100644 index c6a6d25c..00000000 --- a/environment_dev.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: qmllib_dev -channels: - - conda-forge - - defaults -dependencies: - - python==3.12 - - pip - - pip: - - jupytext - - monkeytype - - numpy - - pandas - - pandas - - pre-commit - - pyarrow - - pytest - - pytest-cov - - scikit-learn - - scipy - # build - - build - - meson - - ninja - # publish - - twine diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..d2e20c92 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,17 @@ +# dev +jupytext +monkeytype +numpy +pandas +pip +pre-commit +pytest +pytest-cov +scikit-learn +scipy +# build +build +meson +ninja +# publish +twine