From 4c44c4b08c7c33188cdac933946dc3ba713bd2c5 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Mon, 22 Jul 2024 21:53:42 +0100 Subject: [PATCH 1/5] Move QE conda install into separate stage When building the QEApp Docker container, the Quantum Espresso installation into a conda environment is a step that's completely independent from the others. By putting it into a separate Docker build stage, it can be build in parallel (when using buildkit), as: DOCKER_BUILDKIT=1 docker build . --- .dockerignore | 16 +++++++ Dockerfile | 47 ++++++++++--------- before-notebook.d/70_prepare-qe-executable.sh | 2 +- 3 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..f45fee4b5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +**/*.egg-info +**/__pycache__ +**/.*_cache +**/*.pyc +**/*.tar.gz +*.code-workspace +**/.*.ipynb +**/.ipynb* +.venv/ +build/ +export/ +.do-not-setup-on-localhost + +# Sphinx documentation +docs/html +screenshots/ diff --git a/Dockerfile b/Dockerfile index c53456900..bde81b878 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,26 @@ # syntax=docker/dockerfile:1 -FROM ghcr.io/astral-sh/uv:0.2.18 AS uv -FROM ghcr.io/aiidalab/full-stack:2024.1019 +ARG FULL_STACK_VER=2024.1021 +ARG UV_VER=0.2.27 +ARG QE_VER=7.2 +ARG QE_DIR=/opt/conda/envs/quantum-espresso-${QE_VER} +FROM ghcr.io/astral-sh/uv:0.2.27 AS uv + +# STAGE 1 +# Install QE into conda environment in /opt/conda +# This step is independent from the others and can be run in parallel. +FROM ghcr.io/aiidalab/full-stack:${FULL_STACK_VER} AS qe_conda_env +ARG QE_VER +ARG QE_DIR +RUN mamba create -p "${QE_DIR}" --yes qe="${QE_VER}" && \ + mamba clean --all -f -y && \ + fix-permissions "${CONDA_DIR}" + + +# STAGE 2 +FROM ghcr.io/aiidalab/full-stack:${FULL_STACK_VER} +ARG QE_VER +ARG QE_DIR # Copy whole repo and pre-install the dependencies and app to the tmp folder. # In the before notebook scripts the app will be re-installed by moving it to the app folder. ENV PREINSTALL_APP_FOLDER=${CONDA_DIR}/aiidalab-qe @@ -17,26 +36,12 @@ RUN --mount=from=uv,source=/uv,target=/bin/uv \ cd ${PREINSTALL_APP_FOLDER} && \ # Remove all untracked files and directories. For example the setup lock flag file. git clean -fx && \ - # It is important to install from `aiidalab install` to mimic the exact installation operation as - # from the app store. - # The command wil first install the dependencies from list by parsing setup config files, - # (for `aiidalab/aiidalab<23.03.2` the `setup.py` should be in the root folder of the app https://github.com/aiidalab/aiidalab/pull/382). - # and then the app and restart the daemon in the end. - # But since the aiida profile not yet exists, the daemon restart will fail but it is not a problem. - # Because we only need the dependencies to be installed. - # aiidalab install --yes --python ${CONDA_DIR}/bin/python "quantum-espresso@file://${PREINSTALL_APP_FOLDER}" && \ - # However, have to use `pip install` explicitly because `aiidalab install` call `pip install --user` which will install the app to `/home/${NB_USER}/.local`. - # It won't cause issue for docker but for k8s deployment the home folder is not bind mounted to the pod and the dependencies won't be found. (see issue in `jupyter/docker-stacks` https://github.com/jupyter/docker-stacks/issues/815) uv pip install --system --no-cache . && \ - fix-permissions "${CONDA_DIR}" && \ - fix-permissions "/home/${NB_USER}" - -ENV QE_VERSION="7.2" -RUN mamba create -p /opt/conda/envs/quantum-espresso --yes \ - qe=${QE_VERSION} \ - && mamba clean --all -f -y && \ - fix-permissions "${CONDA_DIR}" && \ - fix-permissions "/home/${NB_USER}" + fix-permissions "${CONDA_DIR}" + +COPY --from=qe_conda_env "${QE_DIR}" "${QE_DIR}" +# TODO: Remove this once we get rid of 70_prepare-qe-executable.sh +ENV QE_VERSION="$QE_VER" # Download the QE pseudopotentials to the folder for afterware installation. ENV PSEUDO_FOLDER=${CONDA_DIR}/pseudo diff --git a/before-notebook.d/70_prepare-qe-executable.sh b/before-notebook.d/70_prepare-qe-executable.sh index 41ba2d391..51d828310 100644 --- a/before-notebook.d/70_prepare-qe-executable.sh +++ b/before-notebook.d/70_prepare-qe-executable.sh @@ -6,7 +6,7 @@ set -x # Copy quantum espresso env to user space. mkdir -p /home/${NB_USER}/.conda/envs if [ ! -d /home/${NB_USER}/.conda/envs/quantum-espresso-${QE_VERSION} ]; then - ln -s /opt/conda/envs/quantum-espresso /home/${NB_USER}/.conda/envs/quantum-espresso-${QE_VERSION} + ln -s /opt/conda/envs/quantum-espresso-${QE_VERSION} /home/${NB_USER}/.conda/envs/quantum-espresso-${QE_VERSION} # Install qe so the progress bar not shown in the notebook when first time using app. echo "Installing qe." From 1b7465855bd4a46c4d79096b8cbd5cd3d08c28fd Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Mon, 22 Jul 2024 22:20:07 +0100 Subject: [PATCH 2/5] Move after pseudos --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index bde81b878..48c48d15b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,15 +39,15 @@ RUN --mount=from=uv,source=/uv,target=/bin/uv \ uv pip install --system --no-cache . && \ fix-permissions "${CONDA_DIR}" -COPY --from=qe_conda_env "${QE_DIR}" "${QE_DIR}" -# TODO: Remove this once we get rid of 70_prepare-qe-executable.sh -ENV QE_VERSION="$QE_VER" - # Download the QE pseudopotentials to the folder for afterware installation. ENV PSEUDO_FOLDER=${CONDA_DIR}/pseudo RUN mkdir -p ${PSEUDO_FOLDER} && \ python -m aiidalab_qe download-pseudos --dest ${PSEUDO_FOLDER} +COPY --from=qe_conda_env "${QE_DIR}" "${QE_DIR}" +# TODO: Remove this once we get rid of 70_prepare-qe-executable.sh +ENV QE_VERSION="$QE_VER" + COPY before-notebook.d/* /usr/local/bin/before-notebook.d/ WORKDIR "/home/${NB_USER}" From d2a75e4dc2766fb187cf6ebe33d090c55266aa68 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Mon, 22 Jul 2024 22:20:39 +0100 Subject: [PATCH 3/5] Max cache mode --- .github/workflows/docker-build-test-upload.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-build-test-upload.yml b/.github/workflows/docker-build-test-upload.yml index c285cf4d0..62664d852 100644 --- a/.github/workflows/docker-build-test-upload.yml +++ b/.github/workflows/docker-build-test-upload.yml @@ -62,7 +62,7 @@ jobs: context: . platforms: linux/amd64 cache-to: | - type=gha,scope=${{ github.workflow }},mode=min + type=gha,scope=${{ github.workflow }},mode=max cache-from: | type=gha,scope=${{ github.workflow }} From 983bd072da48934d46058c533b86148a7475f45d Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Mon, 22 Jul 2024 23:07:53 +0100 Subject: [PATCH 4/5] Support QE conda env in /opt/conda --- before-notebook.d/70_prepare-qe-executable.sh | 11 ++---- src/aiidalab_qe/common/setup_codes.py | 35 +++++++++++-------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/before-notebook.d/70_prepare-qe-executable.sh b/before-notebook.d/70_prepare-qe-executable.sh index 51d828310..1bba44cae 100644 --- a/before-notebook.d/70_prepare-qe-executable.sh +++ b/before-notebook.d/70_prepare-qe-executable.sh @@ -3,14 +3,9 @@ # Debugging. set -x -# Copy quantum espresso env to user space. -mkdir -p /home/${NB_USER}/.conda/envs -if [ ! -d /home/${NB_USER}/.conda/envs/quantum-espresso-${QE_VERSION} ]; then - ln -s /opt/conda/envs/quantum-espresso-${QE_VERSION} /home/${NB_USER}/.conda/envs/quantum-espresso-${QE_VERSION} - - # Install qe so the progress bar not shown in the notebook when first time using app. - echo "Installing qe." +if [[ $(verdi code list -Y localhost -r | wc -l) -eq 0 ]]; then + echo "Installing QE codes..." python -m aiidalab_qe install-qe else - echo "Quantum ESPRESSO app is already installed." + echo "Quantum ESPRESSO codes are already installed." fi diff --git a/src/aiidalab_qe/common/setup_codes.py b/src/aiidalab_qe/common/setup_codes.py index 453e279fa..11ee58d1a 100644 --- a/src/aiidalab_qe/common/setup_codes.py +++ b/src/aiidalab_qe/common/setup_codes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from pathlib import Path from shutil import which from subprocess import CalledProcessError, run @@ -21,9 +20,15 @@ QE_VERSION = "7.2" -CONDA_ENV_PREFIX = Path.home().joinpath( - ".conda", "envs", f"quantum-espresso-{QE_VERSION}" -) + +def get_qe_env(): + # QE is already pre-installed in the QE image + path = Path(f"/opt/conda/envs/quantum-espresso-{QE_VERSION}") + if path.exists(): + return path + else: + return Path.home().joinpath(".conda", "envs", f"quantum-espresso-{QE_VERSION}") + # Add all QE codes with the calcjob entry point in the aiida-quantumespresso. CODE_NAMES = ( @@ -46,7 +51,7 @@ def qe_installed(): - return CONDA_ENV_PREFIX.exists() + return get_qe_env().exists() def install_qe(): @@ -59,7 +64,7 @@ def install_qe(): "--channel", "conda-forge", "--prefix", - str(CONDA_ENV_PREFIX), + str(get_qe_env()), f"qe={QE_VERSION}", ], capture_output=True, @@ -102,9 +107,9 @@ def _generate_string_to_setup_code(code_name, computer_name="localhost"): except NotExistent: label = f"{code_name}-{QE_VERSION}" description = f"{code_name}.x ({QE_VERSION}) setup by AiiDAlab." - filepath_executable = str(CONDA_ENV_PREFIX.joinpath("bin", f"{code_name}.x")) + filepath_executable = get_qe_env().joinpath("bin", f"{code_name}.x") default_calc_job_plugin = f"quantumespresso.{code_name}" - prepend_text = f'eval "$(conda shell.posix hook)"\\nconda activate {CONDA_ENV_PREFIX}\\nexport OMP_NUM_THREADS=1' + prepend_text = f'eval "$(conda shell.posix hook)"\\nconda activate {get_qe_env()}\\nexport OMP_NUM_THREADS=1' python_code = """ computer = load_computer('{}') code = InstalledCode(computer=computer, @@ -116,7 +121,7 @@ def _generate_string_to_setup_code(code_name, computer_name="localhost"): ) code.store() -""".format( +""".format( # noqa: UP032 computer_name, label, description, @@ -134,7 +139,7 @@ def setup_codes(): try: run(["python", "-c", python_code], capture_output=True, check=True) except CalledProcessError as error: - raise RuntimeError(f"Failed to setup codes: {error}") + raise RuntimeError(f"Failed to setup codes: {error}") from None def install(force=False): @@ -175,7 +180,9 @@ def install(force=False): try: install_qe() except CalledProcessError as error: - raise RuntimeError(f"Failed to create conda environment: {error}") + raise RuntimeError( + f"Failed to create conda environment: {error}" + ) from None # After installing QE, we install the corresponding # AiiDA codes: @@ -189,7 +196,7 @@ def install(force=False): yield "Setting up all codes..." run(["python", "-c", python_code], capture_output=True, check=True) except CalledProcessError as error: - raise RuntimeError(f"Failed to setup codes: {error}") + raise RuntimeError(f"Failed to setup codes: {error}") from None except Timeout: # Assume that the installation was triggered by a different process. @@ -198,7 +205,7 @@ def install(force=False): if not codes_are_setup(): raise RuntimeError( "Installation process did not finish in the expected time." - ) + ) from None class QESetupWidget(ipw.VBox): @@ -311,7 +318,7 @@ def _toggle_error_view(self, change): @traitlets.observe("busy") @traitlets.observe("error") @traitlets.observe("installed") - def _update(self, change): + def _update(self, _change): with self.hold_trait_notifications(): if self.hide_by_default: self.layout.visibility = ( From 7afc5f782ba708b541d5ed74152df2c2ea2edf8d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:23:19 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- before-notebook.d/70_prepare-qe-executable.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/before-notebook.d/70_prepare-qe-executable.sh b/before-notebook.d/70_prepare-qe-executable.sh index 76ec8310c..1bba44cae 100644 --- a/before-notebook.d/70_prepare-qe-executable.sh +++ b/before-notebook.d/70_prepare-qe-executable.sh @@ -8,4 +8,4 @@ if [[ $(verdi code list -Y localhost -r | wc -l) -eq 0 ]]; then python -m aiidalab_qe install-qe else echo "Quantum ESPRESSO codes are already installed." -fi \ No newline at end of file +fi