diff --git a/.dockerignore b/.dockerignore index 7eb3e83a..e859feb8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,2 @@ .git/ -docker/ +build diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index 4ac0d69e..33698e38 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -3,12 +3,14 @@ name: CI-Test on: [push, pull_request] jobs: + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + - uses: pre-commit/action@v3.0.0 + test: name: Tests runs-on: ${{ matrix.host-os }} @@ -19,11 +21,16 @@ jobs: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] fail-fast: false steps: + - name: Set env.REPOSITORY_NAME # just the repo, as opposed to org/repo + run: | + export REPOSITORY_NAME=${GITHUB_REPOSITORY#*/} + echo "REPOSITORY_NAME=${REPOSITORY_NAME}" >> $GITHUB_ENV + - name: Checkout the code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -31,11 +38,20 @@ jobs: run: | set -vxeuo pipefail bash ./scripts/install-deps.sh + ls -Al . + ls -Al ./dist/ + tree . + + - uses: actions/upload-artifact@v3 + with: + name: ${{ env.REPOSITORY_NAME }}-wheels + path: dist/*.whl - name: Test with pytest run: | set -vxeuo pipefail bash ./scripts/run-tests.sh + docs: name: Documentation runs-on: ubuntu-latest @@ -46,11 +62,16 @@ jobs: fail-fast: false steps: + - name: Set env.REPOSITORY_NAME # just the repo, as opposed to org/repo + run: | + export REPOSITORY_NAME=${GITHUB_REPOSITORY#*/} + echo "REPOSITORY_NAME=${REPOSITORY_NAME}" >> $GITHUB_ENV + - name: Checkout the code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -63,3 +84,8 @@ jobs: run: | set -vxeuo pipefail bash ./scripts/build-docs.sh + + - uses: actions/upload-artifact@v3 + with: + name: ${{ env.REPOSITORY_NAME }}-docs + path: docs/build/html/ diff --git a/.github/workflows/docs-publish.yml b/.github/workflows/docs-publish.yml index edad7d6c..a24d7ec0 100644 --- a/.github/workflows/docs-publish.yml +++ b/.github/workflows/docs-publish.yml @@ -21,10 +21,10 @@ jobs: echo "REPOSITORY_NAME=${REPOSITORY_NAME}" >> $GITHUB_ENV - name: Checkout the code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -38,6 +38,11 @@ jobs: set -vxeuo pipefail bash ./scripts/build-docs.sh + - uses: actions/upload-artifact@v3 + with: + name: ${{ env.REPOSITORY_NAME }}-docs + path: docs/build/html/ + - name: Deploy documentation to nsls-ii.github.io # We pin to the SHA, not the tag, for security reasons. # https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions diff --git a/.gitignore b/.gitignore index 0b8abecf..9b2c0091 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,7 @@ target/ #pycharm .idea/* +.vscode/* #Ipython Notebook .ipynb_checkpoints diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50653712..bb7d998c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.4.0 hooks: - id: check-yaml - id: check-toml @@ -10,18 +10,16 @@ repos: exclude: | (?x)^( src/| - docs/| - github_deploy_key_nsls_ii_edrixs.enc + docs/ ) - id: trailing-whitespace exclude: | (?x)^( src/| - docs/| - github_deploy_key_nsls_ii_edrixs.enc + docs/ ) - repo: https://github.com/PyCQA/flake8.git - rev: 4.0.1 + rev: 6.1.0 hooks: - id: flake8 exclude: ^examples/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..cd94c850 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.17.3 FATAL_ERROR) # 3.17 > for Python3_SOABI + +project (EDRIXS C Fortran) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type configuration" FORCE) + message(STATUS "Setting default build type: ${CMAKE_BUILD_TYPE}") +endif() + +message(STATUS "CMAKE_COMMAND: ${CMAKE_COMMAND}") +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +find_package(MPI REQUIRED) + +find_package(LAPACK REQUIRED) + +option(EDRIXS_PYINTERFACE "Build python interface" OFF) + +# see https://gitlab.kitware.com/cmake/cmake/-/issues/21779 +if (CMAKE_VERSION VERSION_LESS 3.20) + if(LAPACK_FOUND) + set(_lapack_libs "${LAPACK_LIBRARIES}") + if(_lapack_libs AND TARGET BLAS::BLAS) + # remove the ${BLAS_LIBRARIES} from the interface and replace it + # with the BLAS::BLAS target + list(REMOVE_ITEM _lapack_libs "${BLAS_LIBRARIES}") + list(APPEND _lapack_libs BLAS::BLAS) + endif() + if(_lapack_libs) + set_target_properties(LAPACK::LAPACK PROPERTIES + INTERFACE_LINK_LIBRARIES "${_lapack_libs}" + ) + endif() + unset(_lapack_libs) + endif() +endif() + +# use a custom find module because arpack-ng config module doesn't provide full paths to libs +# see https://github.com/opencollab/arpack-ng/pull/311 +find_package(arpack MODULE REQUIRED COMPONENTS serial parallel) + + +if (EDRIXS_PY_INTERFACE) + message(STATUS "Building python interface") + find_package(Python3 3.7 REQUIRED COMPONENTS Interpreter Development NumPy) + + # Grab the variables from a local Python installation + # F2PY headers + execute_process( + COMMAND "${Python3_EXECUTABLE}" + -c "import numpy.f2py; print(numpy.f2py.get_include())" + OUTPUT_VARIABLE F2PY_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + message(STATUS "Python3_INCLUDE_DIRS: ${Python3_INCLUDE_DIRS}") + message(STATUS "F2PY_INCLUDE_DIR: ${F2PY_INCLUDE_DIR}") + message(STATUS "Python3_NumPy_INCLUDE_DIRS: ${Python3_NumPy_INCLUDE_DIRS}") +endif() + +add_subdirectory(src) diff --git a/README.rst b/README.rst index 294302f8..383d3940 100644 --- a/README.rst +++ b/README.rst @@ -103,31 +103,13 @@ Install from source Be sure to compile OpenBLAS, arpack-ng, mpi4py and edrixs with the same (MPI) Fortran compiler. -* Install Fortran parts of edrixs +* Install edrixs .. code-block:: bash - $ cd src - $ make F90=mpif90 LIBS="-L/usr/local/lib -lopenblas -lparpack -larpack" - $ make install + $ pip install -v . - where, you may need to change ``F90`` and ``LIBS`` according to your specific environment. There will be problems when using gfortran with MKL, so we recommend ``gfortran+OpenBLAS`` or ``ifort+MKL``. ``libedrixsfortran.a`` will be generated, which will be used when building python interface. The executable ``.x`` files will be installed in ``edrixs/bin`` directory and add the following line in ``.bashrc`` or ``.bash_profile`` file, - - .. code-block:: bash - - export PATH=/root_dir_of_edrixs/edrixs/bin:$PATH - -* Install Python parts of edrixs - - Be sure to first make ``libedrixsfortran.a`` in src. - - .. code-block:: bash - - $ python setup.py config_fc --f77exec=mpif90 --f90exec=mpif90 build_ext \ - --libraries=openblas,parpack,arpack --library-dirs=/usr/lib:/usr/local/lib:/opt/local/lib \ - --link-objects=./src/libedrixsfortran.a - $ pip install . - - where, ``--library-dirs`` ares the paths to search ``--libraries``, please set it according to your environments. + There will be problems when using gfortran with MKL, so we recommend ``gfortran+OpenBLAS`` or ``ifort+MKL``. The executable ``.x`` files will be installed in the ``bin`` directory of the active python environment. + The fortran library and compiled python extension are built using ``cmake`` which can be configured by setting the ``CMAKE_CONFIGURE_ARGS`` environment variable. Please see our `online documentation `_ for more details of installation. diff --git a/cmake/Findarpack.cmake b/cmake/Findarpack.cmake new file mode 100644 index 00000000..0367953b --- /dev/null +++ b/cmake/Findarpack.cmake @@ -0,0 +1,50 @@ +include(FindPackageHandleStandardArgs) + +message(STATUS "Finding arpack") + +# if components not specified default to serial library +set(_supported_components serial parallel) +if(NOT arpack_FIND_COMPONENTS) + set(arpack_FIND_COMPONENTS serial) +endif() + +# check request components +foreach(_component ${arpack_FIND_COMPONENTS}) + if (NOT ${_component} IN_LIST _supported_components) + message(FATAL_ERROR "${_component} is not a valid component (serial,parallel)") + endif() +endforeach() + +message(STATUS "${arpack_FIND_COMPONENTS}") + +if(DEFINED ENV{ARPACK_ROOT}) + set(ARPACK_ROOT "$ENV{ARPACK_ROOT}") +endif() + +set(arpack_serial_FOUND FALSE) +set(arpack_parallel_FOUND FALSE) +set(arpack_LIBRARIES "") + +if ("serial" IN_LIST arpack_FIND_COMPONENTS) + find_library(ARPACK_LIBRARY NAMES arpack HINTS ${ARPACK_ROOT}) + if(ARPACK_LIBRARY) + set(arpack_serial_FOUND TRUE) + message(STATUS "ARPACK_LIBRARY: ${ARPACK_LIBRARY}") + add_library(ARPACK::ARPACK INTERFACE IMPORTED) + set_target_properties(ARPACK::ARPACK PROPERTIES INTERFACE_LINK_LIBRARIES "${ARPACK_LIBRARY}") + list(APPEND arpack_LIBRARIES ${ARPACK_LIBRARY}) + endif() +endif() + +if ("parallel" IN_LIST arpack_FIND_COMPONENTS) + find_library(PARPACK_LIBRARY NAMES parpack HINTS ${ARPACK_ROOT}) + if(PARPACK_LIBRARY) + set(arpack_parallel_FOUND TRUE) + message(STATUS "PARPACK_LIBRARY: ${PARPACK_LIBRARY}") + add_library(PARPACK::PARPACK INTERFACE IMPORTED) + set_target_properties(PARPACK::PARPACK PROPERTIES INTERFACE_LINK_LIBRARIES "${PARPACK_LIBRARY}") + list(APPEND arpack_LIBRARIES ${PARPACK_LIBRARY}) + endif() +endif() + +find_package_handle_standard_args(arpack REQUIRED_VARS "arpack_LIBRARIES" HANDLE_COMPONENTS) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1e926b63..20b7c7db 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,20 +1,69 @@ -FROM edrixs/edrixs_base -WORKDIR /project - -COPY . ./src/edrixs - - # build fortran part of edrixs -RUN export LD_LIBRARY_PATH="/usr/local/lib:\$LD_LIBRARY_PATH" \ - && make -C src/edrixs/src F90=mpif90 LIBS="-L/usr/local/lib -lopenblas -lparpack -larpack" \ - && make install -C src/edrixs/src \ - # build python part of edrixs - && cd src/edrixs \ - && python setup.py build_ext --library-dirs=/usr/local/lib \ - && pip install . \ - && cd ../../ \ - # set env - && echo "export PATH=/project/src/edrixs/bin:\$PATH" >> ~/.bashrc \ - && echo "export PATH=/project/src/edrixs/bin:\$PATH" >> /home/rixs/.bashrc \ - # copy examples to /home/rixs - && cp -r src/edrixs/examples /home/rixs/edrixs_examples \ - && chown -R rixs:rixs /home/rixs/edrixs_examples +FROM ubuntu:22.04 as base + +# general environment for docker +ENV DEBIAN_FRONTEND=noninteractive + +# base packages +RUN apt-get update && apt-get install -y --no-install-recommends sudo curl vim libgfortran-11-dev libopenmpi-dev libopenblas-dev python3 libpython3-dev python3-pip python3-venv && rm -rf /var/lib/apt/lists/* + +# create docker user +RUN useradd -m -s /bin/bash -u 1999 docker && echo "docker:docker" | chpasswd && adduser docker sudo + +# enable passwordless sudo +RUN echo "docker ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/docker + +# allow docker user to install into /opt +RUN sudo chown docker:docker /opt + +USER docker +WORKDIR /home/docker + +# create venv +ENV VIRTUAL_ENV=/opt/edrixs +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +FROM base as dep_builder + +ENV LD_LIBRARY_PATH="/opt/edrixs/lib" \ + CMAKE_PREFIX_PATH="/opt/edrixs" + +# install build tools +RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends \ + autoconf \ + build-essential \ + gfortran \ + ca-certificates \ + coreutils \ + curl \ + git \ + cmake + +RUN curl -L https://github.com/opencollab/arpack-ng/archive/refs/tags/3.8.0.tar.gz | tar xvz && \ + cd arpack-ng-3.8.0 && \ + mkdir build && \ + cd build && \ + cmake -DMPI=ON -DCMAKE_INSTALL_PREFIX=/opt/edrixs .. && \ + make -j4 && \ + make test && \ + make install + +FROM dep_builder as app_builder + +RUN pip install --upgrade pip setuptools +RUN pip install numpy scipy sympy matplotlib sphinx mpi4py ipython jupyter jupyterlab + +COPY --chown=docker . edrixs + +RUN export VERBOSE=1 FFLAGS="-Wall -Wunused -Wextra -Wno-maybe-uninitialized -Ofast -faggressive-loop-optimizations -fno-tree-pre" && \ + pip install -v ./edrixs + +from base as app + +# see https://github.com/open-mpi/ompi/issues/4948 +ENV LD_LIBRARY_PATH="/opt/edrixs/lib" \ + CMAKE_PREFIX_PATH="/opt/edrixs" \ + OMPI_MCA_btl_vader_single_copy_mechanism="none" + +COPY --from=app_builder /opt /opt +COPY --from=app_builder --chown=docker /home/docker/edrixs/examples examples diff --git a/docker/Dockerfile_base b/docker/Dockerfile_base deleted file mode 100644 index 5e08cc86..00000000 --- a/docker/Dockerfile_base +++ /dev/null @@ -1,49 +0,0 @@ -FROM ubuntu:18.04 -WORKDIR /project - -RUN apt-get update \ - # add user rixs - && apt-get install -y sudo \ - && useradd -ms /bin/bash rixs \ - && echo "rixs:rixs" | chpasswd \ - && adduser rixs sudo \ - # turn off the error reports from openmpi - && echo "export OMPI_MCA_btl_vader_single_copy_mechanism=none" >> ~/.bashrc \ - && echo "export OMPI_MCA_btl_vader_single_copy_mechanism=none" >> /home/rixs/.bashrc \ - # install deps - && apt-get install -y gcc libgcc-7-dev g++ gfortran ssh wget vim libtool autoconf make \ - && apt-get install -y python3 libpython3-dev python3-pip ipython3 \ - && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \ - && update-alternatives --install /usr/bin/ipython ipython /usr/bin/ipython3 10 \ - && update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 10 \ - # install openblas - && wget https://github.com/xianyi/OpenBLAS/archive/v0.3.6.tar.gz \ - && tar -xzf v0.3.6.tar.gz \ - && make -C OpenBLAS-0.3.6 CC=gcc FC=gfortran \ - && make -C OpenBLAS-0.3.6 PREFIX=/usr/local install \ - && rm -rf OpenBLAS-0.3.6 v0.3.6.tar.gz \ - # install openmpi - && wget https://download.open-mpi.org/release/open-mpi/v3.1/openmpi-3.1.4.tar.bz2 \ - && tar -xjf openmpi-3.1.4.tar.bz2 \ - && cd openmpi-3.1.4 \ - && ./configure CC=gcc CXX=g++ FC=gfortran \ - && make \ - && make install \ - && cd .. \ - && rm -rf openmpi-3.1.4 openmpi-3.1.4.tar.bz2 \ - # install arpack-ng - && wget https://github.com/opencollab/arpack-ng/archive/3.6.3.tar.gz \ - && tar -xzf 3.6.3.tar.gz \ - && cd arpack-ng-3.6.3 \ - && export LD_LIBRARY_PATH="/usr/local/lib:\$LD_LIBRARY_PATH" \ - && ./bootstrap \ - && ./configure --enable-mpi --with-blas="-L/usr/local/lib/ -lopenblas" FC=gfortran F77=gfortran MPIFC=mpif90 MPIF77=mpif90 \ - && make \ - && make install \ - && cd .. \ - && rm -rf arpack-ng-3.6.3 3.6.3.tar.gz \ - # install python deps - && pip install numpy scipy sympy matplotlib sphinx mpi4py jupyter jupyterlab==2.1.3 prompt-toolkit==1.0.15 \ - # set env - && echo "export LD_LIBRARY_PATH=/usr/local/lib:\$LD_LIBRARY_PATH" >> ~/.bashrc \ - && echo "export LD_LIBRARY_PATH=/usr/local/lib:\$LD_LIBRARY_PATH" >> /home/rixs/.bashrc diff --git a/docker/Dockerfile_interactive b/docker/Dockerfile_interactive deleted file mode 100644 index 2e3312da..00000000 --- a/docker/Dockerfile_interactive +++ /dev/null @@ -1,43 +0,0 @@ -FROM edrixs/edrixs_base -WORKDIR /project - -RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \ - && apt-get update \ - && apt-get install -y -q --no-install-recommends curl \ - && curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - \ - && apt install -y git nodejs \ - texlive-xetex texlive-fonts-recommended texlive-plain-generic \ - ffmpeg dvipng cm-super pandoc\ - && pip install ipympl==0.5.* \ - # Also activate ipywidgets extension for JupyterLab - && jupyter nbextension enable --py widgetsnbextension --sys-prefix \ - # Check this URL for most recent compatibilities - # https://github.com/jupyter-widgets/ipywidgets/tree/master/packages/jupyterlab-manager - && jupyter labextension install @jupyter-widgets/jupyterlab-manager@^2.0.0 --no-build \ - && jupyter labextension install @bokeh/jupyter_bokeh@^2.0.0 --no-build \ - && jupyter labextension install jupyter-matplotlib@^0.7.2 --no-build \ - && jupyter lab build -y \ - && jupyter lab clean -y - - # Get edrixs -RUN git clone https://github.com/NSLS-II/edrixs.git \ - && mkdir src \ - && cp -r edrixs src/ - - # build fortran part of edrixs -RUN export LD_LIBRARY_PATH="/usr/local/lib:\$LD_LIBRARY_PATH" \ - && make -C src/edrixs/src F90=mpif90 LIBS="-L/usr/local/lib -lopenblas -lparpack -larpack" \ - && make install -C src/edrixs/src \ - # build python part of edrixs - && cd src/edrixs \ - && python setup.py build_ext --library-dirs=/usr/local/lib \ - && pip install . \ - && cd ../../ \ - # set env - && echo "export PATH=/project/src/edrixs/bin:\$PATH" >> ~/.bashrc \ - && echo "export PATH=/project/src/edrixs/bin:\$PATH" >> /home/rixs/.bashrc \ - # copy examples to /home/rixs - && cp -r src/edrixs/examples /home/rixs/edrixs_examples \ - && chown -R rixs:rixs /home/rixs/edrixs_examples - -CMD jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --allow-root diff --git a/docs/source/user/installation.rst b/docs/source/user/installation.rst index 3d9c35b0..6a03299e 100644 --- a/docs/source/user/installation.rst +++ b/docs/source/user/installation.rst @@ -39,37 +39,48 @@ Several tools and libraries are required to build and install edrixs, Build from source ================= -We will show how to build edrixs from source on Ubuntu Linux 18.04 and macOS Mojave (OSX 10.14) as examples. +We will show how to build edrixs from source on Ubuntu Linux 20.04 and macOS Mojave (OSX 10.14) as examples. We will use gcc, gfortran, openmpi and OpenBLAS in these examples. Building edrixs on other versions of Linux or macOS, or with Intel's ifort+MKL will be similar. -Ubuntu Linux 18.04 +Ubuntu Linux 20.04 ------------------ Install compilers and tools:: sudo apt-get update - sudo apt-get install gcc libgcc-7-dev gfortran g++ - sudo apt-get install git autoconf automake ssh wget - sudo apt-get install python3 libpython3-dev python3-pip + sudo apt-get install build-essential gfortran gcc + sudo apt-get install git wget + sudo apt-get install python3 libpython3-dev python3-pip python3-venv -We will assume ``python`` pointing to ``python3.7`` and ``pip`` pointing to ``pip3.7`` from now on. If this is not the case, you can make links explicitly [#]_ [#]_. +Create and activate a python virtual environment for edrixs:: + + python3 -m venv VIRTUAL_ENV + source VIRTUAL_ENV/bin/activate + +where ``VIRTUAL_ENV`` should be replaced by the directory where you wish to install edrixs. + +Alternatively create and activate a conda environment for edrixs:: + + conda create --name edrixs_env python=3.8 + conda activate edrixs_env + +We will assume ``python`` and ``pip`` are pointing to the activated environment from now on. Check we are using the expected python and pip:: which python - python --version which pip - pip --version + python --version -Install python libraries:: +Fetch the latest version of ``pip``:: - sudo pip install numpy scipy sympy matplotlib + pip install --upgrade pip openmpi, OpenBLAS, ARPACK can be installed by ``apt-get``, but their versions are old and may not work properly. However, they can also be compiled from source easily. In the following, we will show both ways, but we always recommend to build newer ones from source. openmpi can be installed by:: - sudo apt-get install openmpi-bin libopenmpi-dev + sudo apt-get install libopenmpi-dev or from newer version of source, for example v3.1.4:: @@ -151,12 +162,9 @@ Now, we are ready to build edrixs:: git clone https://github.com/NSLS-II/edrixs.git cd edrixs - make -C src F90=mpif90 LIBS="-L/usr/local/lib -lopenblas -lparpack -larpack" - make -C src install - python setup.py config_fc --f77exec=mpif90 --f90exec=mpif90 build_ext --libraries=openblas,parpack,arpack --library-dirs=/usr/local/lib - sudo pip install . + pip install -v . -You can add ``edrixs/bin`` to ``PATH``. Start to play with edrixs by:: +Start to play with edrixs by:: python >>> import edrixs @@ -166,7 +174,7 @@ or go to ``examples`` directory to run some examples:: cd examples/more/ED/14orb ./get_inputs.py - mpirun -np 2 ../../../../src/ed.x + mpirun -np 2 ed.x mpirun -np 2 ./run_fedsolver.py cd ../../RIXS/LaNiO3_thin mpirun -np 2 ./run_rixs_fsolver.py @@ -355,6 +363,4 @@ if no errors, the installation is successful. All done, enjoy! -.. [#] To change your default python you need to add a line to your ``~/.bashrc`` on linux or to your ``~/.bash_profile`` on macOS. This should be ``alias python='/usr/local/bin/python3'`` where the path is determined by calling ``which python3`` from your terminal. - .. [#] To change your default pip you need to add a line to your ``~/.bashrc`` on linux or to your ``~/.bash_profile`` on macOS. This should be ``alias pip='/usr/bin/pip3'`` where the path is determined by calling ``which pip3`` from your terminal. diff --git a/edrixs/scripts.py b/edrixs/scripts.py new file mode 100644 index 00000000..db8614dc --- /dev/null +++ b/edrixs/scripts.py @@ -0,0 +1,46 @@ +def ed(): + from mpi4py import MPI + from .fedrixs import ed_fsolver + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + fcomm = comm.py2f() + + ed_fsolver(fcomm, rank, size) + + +def xas(): + from mpi4py import MPI + from .fedrixs import xas_fsolver + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + fcomm = comm.py2f() + + xas_fsolver(fcomm, rank, size) + + +def rixs(): + from mpi4py import MPI + from .fedrixs import rixs_fsolver + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + fcomm = comm.py2f() + + rixs_fsolver(fcomm, rank, size) + + +def opavg(): + from mpi4py import MPI + from .fedrixs import opavg_fsolver + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + fcomm = comm.py2f() + + opavg_fsolver(fcomm, rank, size) diff --git a/examples/cpc/single_atom/rixs_map_ni.pdf b/examples/cpc/single_atom/rixs_map_ni.pdf deleted file mode 100644 index a8d36775..00000000 Binary files a/examples/cpc/single_atom/rixs_map_ni.pdf and /dev/null differ diff --git a/examples/cpc/single_atom/xas_ni.pdf b/examples/cpc/single_atom/xas_ni.pdf deleted file mode 100644 index adb2a940..00000000 Binary files a/examples/cpc/single_atom/xas_ni.pdf and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..57ea88ed --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel", + "cmake", + "ninja; platform_system!='Windows'", + "numpy", +] +build-backend = "setuptools.build_meta" diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh index ffb6b5a3..ad919dc9 100755 --- a/scripts/install-deps.sh +++ b/scripts/install-deps.sh @@ -1,6 +1,8 @@ #!/bin/bash set -vxeuo pipefail + +sudo apt-get update -y sudo apt-get install -y \ gfortran \ openmpi-bin \ @@ -13,17 +15,13 @@ sudo apt-get install -y \ # These packages are installed in the base environment but may be older # versions. Explicitly upgrade them because they often create # installation problems if out of date. -python -m pip install --upgrade pip "setuptools<=65.5.*" numpy - -# Compile Fortran code -make -C src +python -m pip install --upgrade pip setuptools wheel numpy -# Build Python interface -python setup.py build_ext --inplace -python setup.py build_ext +# # Generate .whl file. +python setup.py sdist bdist_wheel # Install this package and the packages listed in requirements.txt. -pip install . +pip install -v . # Install extra requirements for running tests and building docs. pip install -r requirements-dev.txt diff --git a/setup.cfg b/setup.cfg index a930a7f7..668fccf0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,5 +11,5 @@ f90exec=mpif90 [build_ext] libraries=openblas,parpack,arpack -library-dirs=/usr/lib:/opt/local/lib -link-objects=./src/libedrixsfortran.a +library_dirs=/usr/lib:/opt/local/lib +link_objects=./src/libedrixsfortran.a diff --git a/setup.py b/setup.py index cd024a5b..678a01ab 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,13 @@ -from os import path -from setuptools import find_packages +import os +import subprocess import sys -import versioneer -# It needs f2py, https://www.numpy.org/devdocs/f2py/distutils.html -from numpy.distutils.core import Extension -from numpy.distutils.core import setup +from setuptools import Extension, find_packages, setup +from setuptools.command.build_ext import build_ext + +# needed for setuptools.build_meta to pickup vendored versioneer.py +sys.path.insert(0, os.path.dirname(__file__)) +import versioneer # noqa: E402 # NOTE: This file must remain Python 2 compatible for the foreseeable future, # to ensure that we error out properly for people with outdated setuptools @@ -27,12 +29,12 @@ ) sys.exit(error) -here = path.abspath(path.dirname(__file__)) +here = os.path.abspath(os.path.dirname(__file__)) -with open(path.join(here, "README.rst"), encoding="utf-8") as readme_file: +with open(os.path.join(here, "README.rst"), encoding="utf-8") as readme_file: readme = readme_file.read() -with open(path.join(here, "requirements.txt")) as requirements_file: +with open(os.path.join(here, "requirements.txt")) as requirements_file: # Parse requirements.txt, ignoring any commented-out lines. requirements = [ line @@ -40,19 +42,56 @@ if not line.startswith("#") ] -# Python interface to call fortran subroutines -if "linux" in sys.platform: - extra_link_args = ["-shared"] -else: - extra_link_args = [] -ext_fortran = Extension( - name="edrixs.fedrixs", sources=["src/pyapi.f90"], extra_link_args=extra_link_args -) + +# adapted from https://martinopilia.com/posts/2018/09/15/building-python-extension.html +# see also https://github.com/pyscf/pyscf/blob/master/setup.py +class CMakeExtension(Extension): + def __init__(self, name, cmake_lists_dir=".", sources=[], **kwargs): + Extension.__init__(self, name, sources=sources, **kwargs) + self.cmake_lists_dir = os.path.abspath(cmake_lists_dir) + + +class cmake_build_ext(build_ext): + def build_extensions(self): + # Ensure that CMake is present and working + try: + _ = subprocess.check_output(["cmake", "--version"]) + except OSError: + raise RuntimeError("Cannot find CMake executable") + + for ext in self.extensions: + + extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + + cmake_args = [ + "-DEDRIXS_PY_INTERFACE=ON", + # Ask CMake to place the resulting library in the directory containing the extension + "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}".format(extdir), + # static libraries are placed in a temporary build directory + "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY={}".format(self.build_temp), + # Don't need executables for python lib + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY={}".format(self.build_temp), + ] + + configure_args = os.getenv("CMAKE_CONFIGURE_ARGS") + if configure_args: + cmake_args.extend(configure_args.split(" ")) + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + + # Config + subprocess.check_call( + ["cmake", ext.cmake_lists_dir] + cmake_args, cwd=self.build_temp + ) + + # Build + subprocess.check_call(["cmake", "--build", "."], cwd=self.build_temp) + setup( name="edrixs", version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), description="An open source toolkit for simulating RIXS spectra based on ED", long_description=readme, author="Brookhaven National Lab", @@ -61,7 +100,10 @@ packages=find_packages(exclude=["docs", "tests", "bin", "examples", "src"]), entry_points={ "console_scripts": [ - # 'some.module:some_function', + "ed.x=edrixs.scripts:ed", + "xas.x=edrixs.scripts:xas", + "rixs.x=edrixs.scripts:rixs", + "opavg.x=edrixs.scripts:opavg", ], }, include_package_data=True, @@ -82,5 +124,8 @@ "Natural Language :: English", "Programming Language :: Python :: 3", ], - ext_modules=[ext_fortran], + ext_modules=[ + CMakeExtension("edrixs.placeholder") + ], # edrixs.foo puts build outputs under edrixs subdir + cmdclass={"build_ext": cmake_build_ext}, ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..0465d557 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,67 @@ +set(src + m_constants.f90 + m_control.f90 + m_types.f90 + m_global.f90 + utils.f90 + io.f90 + fock.f90 + spmv.f90 + linsys.f90 + ham.f90 + full_diag.f90 + lanczos.f90 + arpack.f90 + ed_driver.f90 + xas_driver.f90 + rixs_driver.f90 + opavg_driver.f90) + +add_library(edrixs STATIC ${src}) +target_link_libraries(edrixs PUBLIC MPI::MPI_Fortran LAPACK::LAPACK PARPACK::PARPACK) + +install(TARGETS edrixs DESTINATION lib) + +set(progs ed xas rixs opavg) +foreach(prog ${progs}) + add_executable(${prog}.x ${prog}_main.f90) + target_link_libraries(${prog}.x PUBLIC edrixs) + install(TARGETS ${prog}.x DESTINATION bin) +endforeach(prog) + +if (EDRIXS_PY_INTERFACE) + # Vars + set(f2py_module_name fedrixs) + set(fortran_src_file "${CMAKE_CURRENT_SOURCE_DIR}/pyapi.f90") + set(f2py_module_c "${f2py_module_name}module.c") + set(generated_module_file "${f2py_module_name}.${Python3_SOABI}") + + message(STATUS "generated_module_file: ${generated_module_file}") + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" + COMMAND ${Python3_EXECUTABLE} -m "numpy.f2py" + "${fortran_src_file}" + -m ${f2py_module_name} + --lower # Important + DEPENDS ${fortran_src_file} # Fortran source + ) + + # Set up target + add_library(${f2py_module_name} SHARED + "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" # Generated + "${F2PY_INCLUDE_DIR}/fortranobject.c" # From NumPy + "${fortran_src_file}" # Fortran source(s) + ) + + target_include_directories(${f2py_module_name} PUBLIC ${Python3_INCLUDE_DIRS} ${F2PY_INCLUDE_DIR} ${Python3_NumPy_INCLUDE_DIRS}) + target_link_libraries(${f2py_module_name} PUBLIC edrixs) + + set_target_properties( + ${f2py_module_name} + PROPERTIES + PREFIX "" + OUTPUT_NAME ${generated_module_file} + LINKER_LANGUAGE C + ) +endif() diff --git a/src/Makefile b/src/Makefile deleted file mode 100755 index 1cd1fd10..00000000 --- a/src/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -.SUFFIXES: .f90 - -include ./make.sys - -objects=m_constants.o m_control.o m_types.o m_global.o utils.o io.o fock.o spmv.o linsys.o ham.o full_diag.o lanczos.o arpack.o ed_driver.o xas_driver.o rixs_driver.o opavg_driver.o - -default: all - -all: ed xas rixs opavg libedrixs - -ed: $(objects) ed_main.o - $(LINKER) $(objects) ed_main.o -o ed.x $(LIBS) - -xas: $(objects) xas_main.o - $(LINKER) $(objects) xas_main.o -o xas.x $(LIBS) - -rixs: $(objects) rixs_main.o - $(LINKER) $(objects) rixs_main.o -o rixs.x $(LIBS) - -opavg: $(objects) opavg_main.o - $(LINKER) $(objects) opavg_main.o -o opavg.x $(LIBS) - -libedrixs: $(objects) - $(ARCHIVER) libedrixsfortran.a $(objects) - -pylib: $(objects) - $(F2PY) -c $(F2PYFLAGS) -m fedrixs $(F2PYL) pyapi.f90 $(objects) - -.f90.o: - $(F90) $(FFLAGS) $*.f90 - -install: - cp *.x ../bin - -clean: - rm -f *.mod - rm -f *.o - rm -f *.so - rm -rf *.so.dSYM - rm -f *.x - rm -f *.a - rm -f ../bin/*.x - diff --git a/src/make.sys b/src/make.sys deleted file mode 100644 index 62f5bf92..00000000 --- a/src/make.sys +++ /dev/null @@ -1,35 +0,0 @@ -# There may be problems when using Intel's compilers and MKL with f2py, -# so we strongly recommend to use gfortran+OpenBLAS. - -# If you use conda, please be sure to remove mkl mkl-service, and re-install -# numpy, scipy, matplotlib by "conda install nomkl numpy scipy matplotlib". - -# On Linux, one can compile OpenBLAS from scratch with gfortran compiler, -# then compile arpack-ng from scratch with gfortran and OpenBLAS. - -# On MacOSX, one can use the package tool MacPorts to install everything, -# use "sudo port install arpack+openblas+mpich" to install -# the mpif90 compiler with gfortran, openblas, libarpack and libparapck. - -#------------------------------------------------------------------------- -# MPI+gfortran compiler -F90 = mpif90 -LINKER = $(F90) -ARCHIVER = ar -ruv - -#------------------------------------------------------------------------- -CPP = -cpp -CHECK = -Wall -Wunused -Wextra #-fbacktrace -fcheck=all -g -CDUMP = -Wno-maybe-uninitialized #-fopt-info -LEVEL = -Ofast -faggressive-loop-optimizations -fno-tree-pre -fPIC - -#------------------------------------------------------------------------- -FFLAGS = -c $(CPP) $(CHECK) $(CDUMP) $(LEVEL) - -#------------------------------------------------------------------------- -#LIBS = -L/opt/local/lib -lparpack -larpack -lopenblas # on MacOSX using MacPorts -LIBS = -llapack -lopenblas -lparpack -larpack -F2PY = f2py -F2PYC = $(F90) -F2PYL = $(LIBS) -F2PYFLAGS = --fcompiler=gnu95 --f90exec=$(F90) --opt='-O3' diff --git a/src/make.sys.gfortran b/src/make.sys.gfortran deleted file mode 100644 index c55d8429..00000000 --- a/src/make.sys.gfortran +++ /dev/null @@ -1,34 +0,0 @@ -# There may be problems when using Intel's compilers and MKL with f2py, -# so we strongly recommend to use gfortran+OpenBLAS. - -# If you use conda, please be sure to remove mkl mkl-service, and re-install -# numpy, scipy, matplotlib by "conda install nomkl numpy scipy matplotlib". - -# On Linux, one can compile OpenBLAS from scratch with gfortran compiler, -# then compile arpack-ng from scratch with gfortran and OpenBLAS. - -# On MacOSX, one can use the package tool MacPorts to install everything, -# use "sudo port install arpack+openblas+mpich" to install -# the mpif90 compiler with gfortran, openblas, libarpack and libparapck. - -#------------------------------------------------------------------------- -# MPI+gfortran compiler -F90 = mpif90 -LINKER = $(F90) -ARCHIVER = ar -ruv - -#------------------------------------------------------------------------- -CPP = -cpp -CHECK = -Wall -Wunused -Wextra #-fbacktrace -fcheck=all -g -CDUMP = -Wno-maybe-uninitialized #-fopt-info -LEVEL = -Ofast -faggressive-loop-optimizations -fno-tree-pre -fPIC - -#------------------------------------------------------------------------- -FFLAGS = -c $(CPP) $(CHECK) $(CDUMP) $(LEVEL) - -#------------------------------------------------------------------------- -LIBS = -L/opt/local/lib -lparpack -larpack -lopenblas # on MacOSX using MacPorts -F2PY = f2py -F2PYC = $(F90) -F2PYL = $(LIBS) -F2PYFLAGS = --fcompiler=gnu95 --f90exec=$(F90) --opt='-O3' diff --git a/src/make.sys.ifort b/src/make.sys.ifort deleted file mode 100644 index 7c46c0f8..00000000 --- a/src/make.sys.ifort +++ /dev/null @@ -1,24 +0,0 @@ -# Use Intel's ifort + MKL -# Be sure to compile arpack-ng with the same ifort + MKL. - -#------------------------------------------------------------------------- -# MPI+ifort compiler -F90 = mpif90 -LINKER = $(F90) -ARCHIVER = ar -ruv - -#------------------------------------------------------------------------- -CPP = -fpp -CHECK = -warn all #-check all,noarg_temp_created -traceback -g -CDUMP = -nogen-interfaces -LEVEL = -O3 -xHost -fPIC - -#------------------------------------------------------------------------- -FFLAGS = -c $(CPP) $(CHECK) $(CDUMP) $(LEVEL) - -#------------------------------------------------------------------------- -LIBS = -L${MKLROOT}/lib/intel64 -lmkl_core -lmkl_sequential -lmkl_rt -L/home/yilinwang/soft/arpack-ng-3.6.2/lib -lparpack -larpack -F2PY = f2py -F2PYC = $(F90) -F2PYL = $(LIBS) -F2PYFLAGS = --fcompiler=intelem --f90exec=$(F90) --opt='-O3'