diff --git a/.clang-tidy b/.clang-tidy index a996e64c0a..8d269d52fa 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -10,6 +10,8 @@ Checks: | -bugprone-implicit-widening-of-multiplication-result, -bugprone-macro-parentheses, -bugprone-reserved-identifier, + -bugprone-switch-missing-default-case, + -bugprone-unchecked-optional-access, clang-analyzer-alpha.*, modernize-deprecated-headers, modernize-make-shared, @@ -41,7 +43,6 @@ Checks: | readability-function-size' WarningsAsErrors: '*,-clang-analyzer-core.StackAddrEscapeBase,-clang-analyzer-optin.mpi.MPI-Checker' HeaderFilterRegex: '.*' -AnalyzeTemporaryDtors: false FormatStyle: none User: espresso CheckOptions: diff --git a/.github/actions/build_and_check/action.yml b/.github/actions/build_and_check/action.yml index 6a4e4d6e71..0e9546d98b 100644 --- a/.github/actions/build_and_check/action.yml +++ b/.github/actions/build_and_check/action.yml @@ -1,24 +1,15 @@ name: 'Build and check' -description: 'Build espresso and run checks' -inputs: - asan: # id of input - description: 'Whether to build with address sanitizer' - required: true - default: 'false' - ubsan: - description: 'Whether to build with undefined behavior sanitizer' - required: true - default: 'false' +description: 'Build ESPResSo and run checks' runs: using: "composite" steps: - run: | brew install boost boost-mpi fftw brew install hdf5-mpi - pip3 install -c requirements.txt numpy cython h5py scipy + pip3 install -c requirements.txt "cython<3.0" numpy scipy h5py packaging shell: bash - run: | - export myconfig=maxset with_cuda=false test_timeout=800 with_asan=${{ inputs.asan }} with_ubsan=${{ inputs.ubsan }} check_skip_long=true + export myconfig=maxset with_cuda=false with_gsl=false test_timeout=800 check_skip_long=true bash maintainer/CI/build_cmake.sh shell: bash # This is a workaround for the unfortunate interaction of MacOS and OpenMPI 4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 06de904ae7..7ab35c52b7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,7 @@ jobs: - name: Install pandoc uses: r-lib/actions/setup-pandoc@v2 - name: Setup SSH agent - uses: webfactory/ssh-agent@v0.7.0 + uses: webfactory/ssh-agent@v0.9.0 with: ssh-private-key: ${{ secrets.GH_PAGES_SSH_PRIVATE_KEY }} - name: Checkout diff --git a/.github/workflows/push_pull.yml b/.github/workflows/push_pull.yml index 82229ac665..b16c9752e0 100644 --- a/.github/workflows/push_pull.yml +++ b/.github/workflows/push_pull.yml @@ -1,57 +1,24 @@ -name: run tests on mac +name: run tests on macOS on: push: pull_request: - schedule: - - cron: '0 3 * * *' permissions: contents: read # to fetch code (actions/checkout) jobs: - regular_check: - runs-on: macos-12 - if: github.event_name != 'schedule' + macos: + runs-on: macos-13 steps: - name: Checkout uses: actions/checkout@main - name: Setup Python environment - uses: actions/setup-python@v4.3.1 + uses: actions/setup-python@v5.1.0 with: - python-version: '3.8' - - name: Check without sanitizer + python-version: '3.9' + - name: Build and check uses: ./.github/actions/build_and_check - with: - asan: false - ubsan: false - - sanitizer_check: - permissions: - contents: read # to fetch code (actions/checkout) - issues: write # to create an issue - - runs-on: macos-12 - if: (github.event_name == 'schedule' && github.repository == 'espressomd/espresso') - steps: - - name: Checkout - uses: actions/checkout@main - - name: Setup Python environment - uses: actions/setup-python@v4.3.1 - with: - python-version: '3.8' - - name: Check with sanitizer - uses: ./.github/actions/build_and_check - with: - asan: true - ubsan: true - - name: Setting job link variable - if: ${{ failure() }} - run: | - echo "job_link=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> $GITHUB_ENV - - uses: alialaa/issue-action@v1 - if: ${{ failure() }} - with: - token: ${{ secrets.GITHUB_TOKEN }} - title: Scheduled CI job has failed - body: ${{ env.job_link }} + env: + build_procs: 4 + check_procs: 4 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1b257ea521..873d6e86be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -166,6 +166,21 @@ debian:10: - espresso - no-cuda +debian:12: + <<: *global_job_definition + stage: build + image: ghcr.io/espressomd/docker/debian:f7f8ef2c0ca93c67aa16b9f91785492fb04ecc1b + variables: + with_cuda: 'false' + myconfig: 'maxset' + make_check_python: 'false' + with_stokesian_dynamics: 'true' + script: + - bash maintainer/CI/build_cmake.sh + tags: + - espresso + - no-cuda + fedora:36: <<: *global_job_definition stage: build @@ -181,6 +196,23 @@ fedora:36: - espresso - no-cuda +fedora:40: + <<: *global_job_definition + stage: build + image: ghcr.io/espressomd/docker/fedora:f7f8ef2c0ca93c67aa16b9f91785492fb04ecc1b + variables: + with_cuda: 'false' + with_gsl: 'false' + myconfig: 'maxset' + make_check_python: 'true' + with_stokesian_dynamics: 'true' + cmake_params: '-D CMAKE_INCLUDE_PATH=/usr/include/mpich-x86_64 -D CMAKE_PREFIX_PATH=/usr/lib64/mpich/lib/' + script: + - bash maintainer/CI/build_cmake.sh + tags: + - espresso + - no-cuda + ### Builds with CUDA clang-sanitizer: @@ -195,7 +227,7 @@ clang-sanitizer: with_coverage: 'false' with_static_analysis: 'true' check_skip_long: 'true' - with_asan: 'true' + with_asan: 'false' with_ubsan: 'true' with_scafacos: 'true' with_stokesian_dynamics: 'true' @@ -227,6 +259,28 @@ fast_math: - cuda when: manual +cuda12-maxset-ubuntu24.04: + <<: *global_job_definition + stage: build + image: ghcr.io/espressomd/docker/ubuntu:f7f8ef2c0ca93c67aa16b9f91785492fb04ecc1b + variables: + CC: 'gcc-12' + CXX: 'g++-12' + GCOV: 'gcov-12' + myconfig: 'maxset' + with_cuda: 'true' + with_coverage: 'false' + with_coverage_python: 'false' + check_skip_long: 'false' + with_scafacos: 'true' + with_stokesian_dynamics: 'true' + script: + - bash maintainer/CI/build_cmake.sh + tags: + - espresso + - cuda + - numa + cuda11-maxset-ubuntu20.04: <<: *global_job_definition stage: build @@ -238,6 +292,7 @@ cuda11-maxset-ubuntu20.04: myconfig: 'maxset' with_cuda: 'true' with_coverage: 'true' + with_coverage_python: 'true' check_skip_long: 'true' with_scafacos: 'true' with_stokesian_dynamics: 'true' @@ -259,6 +314,7 @@ cuda11-maxset-ubuntu22.04: myconfig: 'maxset' with_cuda: 'true' with_coverage: 'true' + with_coverage_python: 'true' check_skip_long: 'true' with_scafacos: 'true' with_stokesian_dynamics: 'true' @@ -489,6 +545,7 @@ run_tutorials: paths: - build/doc/tutorials expire_in: 1 week + timeout: 2h tags: - espresso - cuda diff --git a/CMakeLists.txt b/CMakeLists.txt index 84aa170e0e..5f4e516320 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,14 @@ if(POLICY CMP0025) # make CXX_COMPILER_ID return "AppleClang" instead of "Clang" for Apple Clang cmake_policy(SET CMP0025 NEW) endif() +if(POLICY CMP0146) + # make FindCUDA available + cmake_policy(SET CMP0146 OLD) +endif() +if(POLICY CMP0148) + # make FindPythonInterp available + cmake_policy(SET CMP0148 OLD) +endif() # CMake modules/macros are in a subdirectory to keep this file cleaner set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) @@ -47,7 +55,7 @@ if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) endif() -set(PROJECT_VERSION "4.2.1") +set(PROJECT_VERSION "4.2-dev") # # CMake internal vars diff --git a/Readme.md b/Readme.md index c1b4e11880..142b0d7e12 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ # ESPResSo -[![GitLab CI](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/badges/python/pipeline.svg)](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/commits/python) +[![GitLab CI](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/badges/4.2/pipeline.svg)](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/-/commits/4.2) [![codecov](https://codecov.io/gh/espressomd/espresso/branch/python/graph/badge.svg)](https://codecov.io/gh/espressomd/espresso) This is the Molecular Dynamics software ESPResSo ("Extensible diff --git a/doc/sphinx/installation.rst b/doc/sphinx/installation.rst index 23c7de6794..a7e3bc84b6 100644 --- a/doc/sphinx/installation.rst +++ b/doc/sphinx/installation.rst @@ -8,10 +8,10 @@ This chapter will describe how to get, compile and run the software. |es| releases are available as source code packages from the homepage [1]_. This is where new users should get the code. The code within release packages is tested and known to run on a number of platforms. -Alternatively, people that want to use the newest features of |es| or that -want to start contributing to the software can instead obtain the +Alternatively, people who want to use the newest features of |es| or +start contributing to the software can instead obtain the current development code via the version control system software [2]_ -from |es|'s project page at Github [3]_. This code might be not as well +from |es|'s project page at GitHub [3]_. This code might be not as well tested and documented as the release code; it is recommended to use this code only if you have already gained some experience in using |es|. @@ -25,7 +25,7 @@ performance of the code. Therefore it is not possible to build a single binary that can satisfy all needs. For performance reasons a user should always activate only those features that are actually needed. This means, however, that learning how to compile is a necessary evil. -The build system of |es| uses CMake [4]_ to compile +The build system of |es| uses CMake to compile software easily on a wide range of platforms. .. _Requirements: @@ -39,7 +39,7 @@ are required to be able to compile and use |es|: .. glossary:: CMake - The build system is based on CMake. + The build system is based on CMake version 3 or later [4]_. C++ compiler The C++ core of |es| needs to be built by a C++14-capable compiler. @@ -52,6 +52,11 @@ are required to be able to compile and use |es|: For some algorithms like P\ :math:`^3`\ M, |es| needs the FFTW library version 3 or later [5]_ for Fourier transforms, including header files. + CUDA + For some algorithms like P\ :math:`^3`\ M, + |es| provides GPU-accelerated implementations for NVIDIA GPUs. + We strongly recommend CUDA 12.0 or later [6]_. + MPI An MPI library that implements the MPI standard version 1.2 is required to run simulations in parallel. |es| is currently tested against @@ -73,9 +78,30 @@ are required to be able to compile and use |es|: Python |es|'s main user interface relies on Python 3. + We strongly recommend using Python environments to isolate + packages required by |es| from packages installed system-wide. + This can be achieved using venv [7]_, conda [8]_, or any similar tool. + Inside an environment, commands of the form + ``sudo apt install python3-numpy python3-scipy`` + can be rewritten as ``python3 -m pip install numpy scipy``, + and thus do not require root privileges. + + Depending on your needs, you may choose to install all |es| + dependencies inside the environment, or only the subset of + dependencies not already satisfied by your workstation or cluster. + For the exact syntax to create and configure an environment, + please refer to the tool documentation. + Cython Cython is used for connecting the C++ core to Python. + Python environment tools may allow you to install a Python executable + that is more recent than the system-wide Python executable. + Be aware this might lead to compatibility issues if Cython + accidentally picks up the system-wide :file:`Python.h` header file. + In that scenario, you will have to manually adapt the C++ compiler + include paths to find the correct :file:`Python.h` header file. + .. _Installing requirements on Ubuntu Linux: @@ -86,9 +112,9 @@ To compile |es| on Ubuntu 22.04 LTS, install the following dependencies: .. code-block:: bash - sudo apt install build-essential cmake cython3 python3-pip python3-numpy \ - libboost-all-dev openmpi-common fftw3-dev libhdf5-dev libhdf5-openmpi-dev \ - python3-scipy python3-opengl libgsl-dev freeglut3 + sudo apt install build-essential cmake cython3 python3-dev openmpi-bin \ + libboost-all-dev fftw3-dev libfftw3-mpi-dev libhdf5-dev libhdf5-openmpi-dev \ + python3-pip python3-numpy python3-scipy python3-opengl libgsl-dev freeglut3 Optionally the ccmake utility can be installed for easier configuration: @@ -188,7 +214,7 @@ To use Jupyter Notebook, install the following packages: .. code-block:: bash - pip3 install --user nbformat notebook 'jupyter_contrib_nbextensions==0.5.1' + pip3 install --user 'nbformat==5.1.3' 'nbconvert==6.4.5' 'notebook==6.4.8' 'jupyter_contrib_nbextensions==0.5.1' jupyter contrib nbextension install --user jupyter nbextension enable rubberband/main jupyter nbextension enable exercise2/main @@ -226,11 +252,11 @@ Installing requirements on Windows via WSL To run |es| on Windows, use the Linux subsystem. For that you need to -* follow `these instructions `__ to install Ubuntu -* start Ubuntu (or open an Ubuntu tab in `Windows Terminal `__) +* follow `these instructions `__ to install Ubuntu +* start Ubuntu (or open an Ubuntu tab in `Windows Terminal `__) * execute ``sudo apt update`` to prepare the installation of dependencies * optional step: If you have a NVIDIA graphics card available and want to make - use of |es|'s GPU acceleration, follow `these instructions `__ + use of |es|'s GPU acceleration, follow `these instructions `__ to set up CUDA. * follow the instructions for :ref:`Installing requirements on Ubuntu Linux` @@ -404,15 +430,15 @@ General features - ``THERMOSTAT_PER_PARTICLE`` Allows setting a per-particle friction coefficient for the Langevin and Brownian thermostats. -- ``ROTATIONAL_INERTIA`` +- ``ROTATIONAL_INERTIA`` Allows particles to have individual rotational inertia matrix eigenvalues. + When not built in, all eigenvalues are unity in simulation units. - ``EXTERNAL_FORCES`` Allows to define an arbitrary constant force for each particle individually. Also allows to fix individual coordinates of particles, keep them at a fixed position or within a plane. -- ``MASS`` Allows particles to have individual masses. Note that some analysis - procedures have not yet been adapted to take the masses into account - correctly. +- ``MASS`` Allows particles to have individual masses. + When not built in, all masses are unity in simulation units. .. seealso:: :attr:`espressomd.particle_data.ParticleHandle.mass` @@ -971,3 +997,12 @@ ____ .. [5] https://www.fftw.org/ + +.. [6] + https://docs.nvidia.com/cuda/ + +.. [7] + https://docs.python.org/3/library/venv.html + +.. [8] + https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html diff --git a/doc/sphinx/integration.rst b/doc/sphinx/integration.rst index dfe66078b0..5517bb01ab 100644 --- a/doc/sphinx/integration.rst +++ b/doc/sphinx/integration.rst @@ -199,6 +199,7 @@ Notes: * The particle forces :math:`F` include interactions as well as a friction (:math:`\gamma^0`) and noise term (:math:`\sqrt{k_B T \gamma^0 dt} \overline{\eta}`) analogous to the terms in the :ref:`Langevin thermostat`. * The particle forces are only calculated in step 5 and then reused in step 1 of the next iteration. See :ref:`Velocity Verlet Algorithm` for the implications of that. * The NpT algorithm doesn't support :ref:`Lees-Edwards boundary conditions`. +* The NpT algorithm doesn't support propagation of angular velocities. .. _Steepest descent: diff --git a/doc/sphinx/io.rst b/doc/sphinx/io.rst index 47f3009360..a05fc2c780 100644 --- a/doc/sphinx/io.rst +++ b/doc/sphinx/io.rst @@ -111,11 +111,6 @@ Be aware of the following limitations: for a specific combination of features, please share your findings with the |es| community. -* Checkpointing only supports recursion on the head node. It is therefore - impossible to checkpoint a :class:`espressomd.system.System` instance that - contains LB boundaries, constraints or auto-update accumulators when the - simulation is running with 2 or more MPI nodes. - * The active actors, i.e., the content of ``system.actors``, are checkpointed. For lattice-Boltzmann fluids, this only includes the parameters such as the lattice constant (``agrid``). The actual flow field has to be saved diff --git a/doc/sphinx/particles.rst b/doc/sphinx/particles.rst index 31d9cf180f..527e03454b 100644 --- a/doc/sphinx/particles.rst +++ b/doc/sphinx/particles.rst @@ -386,6 +386,7 @@ For correct results, the LB thermostat has to be deactivated for virtual sites:: system.thermostat.set_lb(kT=0, act_on_virtual=False) Please note that the velocity attribute of the virtual particles does not carry valid information for this virtual sites scheme. +With the LB GPU implementation, inertialess tracers only work on 1 MPI rank. .. _Interacting with groups of particles: diff --git a/doc/tutorials/constant_pH/constant_pH.ipynb b/doc/tutorials/constant_pH/constant_pH.ipynb index dc3f8519e0..493484c201 100644 --- a/doc/tutorials/constant_pH/constant_pH.ipynb +++ b/doc/tutorials/constant_pH/constant_pH.ipynb @@ -137,10 +137,10 @@ "plt.rcParams.update({'font.size': 18})\n", "\n", "import numpy as np\n", - "import setuptools\n", + "import pkg_resources\n", "import pint # module for working with units and dimensions\n", "import time\n", - "assert setuptools.version.pkg_resources.packaging.specifiers.SpecifierSet('>=0.10.1').contains(pint.__version__), \\\n", + "assert pkg_resources.packaging.specifiers.SpecifierSet('>=0.10.1').contains(pint.__version__), \\\n", " f'pint version {pint.__version__} is too old: several numpy operations can cast away the unit'\n", "\n", "import espressomd\n", diff --git a/doc/tutorials/error_analysis/error_analysis_part2.ipynb b/doc/tutorials/error_analysis/error_analysis_part2.ipynb index 0dfda3cc48..cbce23701c 100644 --- a/doc/tutorials/error_analysis/error_analysis_part2.ipynb +++ b/doc/tutorials/error_analysis/error_analysis_part2.ipynb @@ -204,7 +204,7 @@ "fig = plt.figure(figsize=(10, 6))\n", "plt.plot(autocov)\n", "plt.xlabel(\"lag time $j$\")\n", - "plt.ylabel(\"$\\hat{K}^{XX}_j$\")\n", + "plt.ylabel(r\"$\\hat{K}^{XX}_j$\")\n", "plt.show()\n", "```" ] @@ -243,7 +243,7 @@ "plt.gca().axhline(0, color=\"gray\", linewidth=1)\n", "plt.plot(autocov)\n", "plt.xlabel(\"lag time $j$\")\n", - "plt.ylabel(\"$\\hat{K}^{XX}_j$\")\n", + "plt.ylabel(r\"$\\hat{K}^{XX}_j$\")\n", "plt.show()" ] }, @@ -293,7 +293,7 @@ "plt.xlim((1, N_MAX))\n", "plt.xscale(\"log\")\n", "plt.xlabel(\"lag time $j$\")\n", - "plt.ylabel(\"$\\hat{K}^{XX}_j$\")\n", + "plt.ylabel(r\"$\\hat{K}^{XX}_j$\")\n", "plt.legend()\n", "plt.show()\n", "\n", @@ -482,7 +482,7 @@ " plt.gca().axhline(0, color=\"gray\",linewidth=1)\n", " plt.plot(acf)\n", " plt.xlabel(\"lag time $j$\")\n", - " plt.ylabel(\"$\\hat{K}^{XX}_j$\")\n", + " plt.ylabel(r\"$\\hat{K}^{XX}_j$\")\n", " plt.show()\n", "\n", " # create integrated ACF plot\n", diff --git a/doc/tutorials/langevin_dynamics/langevin_dynamics.ipynb b/doc/tutorials/langevin_dynamics/langevin_dynamics.ipynb index 859fe4f493..a01a418b3b 100644 --- a/doc/tutorials/langevin_dynamics/langevin_dynamics.ipynb +++ b/doc/tutorials/langevin_dynamics/langevin_dynamics.ipynb @@ -481,7 +481,7 @@ "```python\n", "plt.figure(figsize=(10, 6))\n", "plt.xlabel(r'$\\gamma$')\n", - "plt.ylabel('Diffusion coefficient [$\\sigma^2/t$]')\n", + "plt.ylabel(r'Diffusion coefficient [$\\sigma^2/t$]')\n", "x = np.linspace(0.9 * min(gammas), 1.1 * max(gammas), 50)\n", "y = KT / x\n", "plt.plot(x, y, '-', label=r'$k_\\mathrm{B}T\\gamma^{-1}$')\n", diff --git a/doc/tutorials/lennard_jones/lennard_jones.ipynb b/doc/tutorials/lennard_jones/lennard_jones.ipynb index a4ac80f4bc..c4fb7fb2d9 100644 --- a/doc/tutorials/lennard_jones/lennard_jones.ipynb +++ b/doc/tutorials/lennard_jones/lennard_jones.ipynb @@ -121,8 +121,8 @@ "plt.plot(xs, ys_lj, label='LJ')\n", "plt.plot(xs, ys_WCA, label='WCA')\n", "plt.axhline(y=0, color='grey')\n", - "plt.xlabel(\"$r/\\sigma$\")\n", - "plt.ylabel(\"$V(r)/(k_{\\mathrm{B}}T)$\")\n", + "plt.xlabel(r\"$r/\\sigma$\")\n", + "plt.ylabel(r\"$V(r)/(k_{\\mathrm{B}}T)$\")\n", "plt.legend()\n", "plt.ylim(-1.5, 2.5)\n", "plt.show()" diff --git a/doc/tutorials/polymers/polymers.ipynb b/doc/tutorials/polymers/polymers.ipynb index 7e6a4b5153..526b52061f 100644 --- a/doc/tutorials/polymers/polymers.ipynb +++ b/doc/tutorials/polymers/polymers.ipynb @@ -631,7 +631,7 @@ " ls='', marker='o', capsize=5, capthick=1,\n", " label=r'$R_g^{\\mathrm{simulation}}$')\n", "plt.xlabel('Number of monomers $N$')\n", - "plt.ylabel('Radius of gyration [$\\sigma$]')\n", + "plt.ylabel(r'Radius of gyration [$\\sigma$]')\n", "plt.legend()\n", "plt.show()" ] @@ -692,7 +692,7 @@ " ls='', marker='o', capsize=5, capthick=1,\n", " label=r'$R_h^{\\mathrm{simulation}}$')\n", "plt.xlabel('Number of monomers $N$')\n", - "plt.ylabel('Hydrodynamic radius [$\\sigma$]')\n", + "plt.ylabel(r'Hydrodynamic radius [$\\sigma$]')\n", "plt.legend()\n", "plt.show()" ] diff --git a/maintainer/benchmarks/lb.py b/maintainer/benchmarks/lb.py index 6aede3a323..f41ace6859 100644 --- a/maintainer/benchmarks/lb.py +++ b/maintainer/benchmarks/lb.py @@ -38,6 +38,8 @@ type=float, default=0.03, required=False, help="Fraction of the simulation box volume occupied by " "particles (range: [0.01-0.74], default: 0.50)") +parser.add_argument("--gpu", default=False, action="store_true") +parser.add_argument("--no-gpu", dest="gpu", action="store_false") parser.add_argument("--output", metavar="FILEPATH", action="store", type=str, required=False, default="benchmarks.csv", help="Output file (default: benchmarks.csv)") @@ -51,6 +53,8 @@ "volume_fraction exceeds the physical limit of sphere packing (~0.74)" required_features = ["LENNARD_JONES"] +if args.gpu: + required_features.append("CUDA") espressomd.assert_features(required_features) # System @@ -66,8 +70,7 @@ # System parameters ############################################################# - -n_proc = system.cell_system.get_state()['n_nodes'] +n_proc = system.cell_system.get_state()["n_nodes"] n_part = n_proc * args.particles_per_core # volume of N spheres with radius r: N * (4/3*pi*r^3) box_l = (n_part * 4. / 3. * np.pi * (lj_sig / 2.)**3 @@ -84,7 +87,6 @@ ############################################################# system.time_step = 0.01 system.cell_system.skin = 0.5 -system.thermostat.turn_off() # Interaction setup ############################################################# @@ -121,14 +123,11 @@ system.thermostat.turn_off() print(f"LB shape: [{lb_grid}, {lb_grid}, {lb_grid}]") print(f"LB agrid: {agrid:.3f}") -if hasattr(espressomd.lb, "LBFluid"): - LBClass = espressomd.lb.LBFluid -elif hasattr(espressomd.lb, "LBFluidWalberla"): - LBClass = espressomd.lb.LBFluidWalberla -else: - raise Exception("LB not built in") - -lbf = LBClass(agrid=agrid, dens=1, visc=1, tau=system.time_step, kT=1, seed=1) + +lb_class = espressomd.lb.LBFluid +if args.gpu: + lb_class = espressomd.lb.LBFluidGPU +lbf = lb_class(agrid=agrid, dens=1, visc=1, tau=system.time_step, kT=1, seed=1) system.actors.add(lbf) system.thermostat.set_lb(gamma=10, LB_fluid=lbf, seed=2) diff --git a/maintainer/benchmarks/lj.py b/maintainer/benchmarks/lj.py index a32737e02e..c8d3fa9c07 100644 --- a/maintainer/benchmarks/lj.py +++ b/maintainer/benchmarks/lj.py @@ -73,8 +73,7 @@ # System parameters ############################################################# - -n_proc = system.cell_system.get_state()['n_nodes'] +n_proc = system.cell_system.get_state()["n_nodes"] n_part = n_proc * args.particles_per_core # volume of N spheres with radius r: N * (4/3*pi*r^3) box_l = (n_part * 4. / 3. * np.pi * (lj_sig / 2.)**3 @@ -88,7 +87,6 @@ ############################################################# system.time_step = 0.01 system.cell_system.skin = 0.5 -system.thermostat.turn_off() # Interaction setup ############################################################# diff --git a/maintainer/benchmarks/mc_acid_base_reservoir.py b/maintainer/benchmarks/mc_acid_base_reservoir.py index 0557e53b3e..e9ba6d09aa 100644 --- a/maintainer/benchmarks/mc_acid_base_reservoir.py +++ b/maintainer/benchmarks/mc_acid_base_reservoir.py @@ -24,7 +24,7 @@ import espressomd import espressomd.electrostatics import espressomd.reaction_methods -import setuptools +import pkg_resources import argparse parser = argparse.ArgumentParser(description="Benchmark MC simulations in the grand-reaction ensemble. " @@ -45,7 +45,7 @@ # process and check arguments assert args.particles_per_core >= 100, "you need to use at least 100 particles per core to avoid finite-size effects in the simulation" espressomd.assert_features(['WCA', 'ELECTROSTATICS']) -assert setuptools.version.pkg_resources.packaging.specifiers.SpecifierSet('>=0.10.1').contains(pint.__version__), \ +assert pkg_resources.packaging.specifiers.SpecifierSet('>=0.10.1').contains(pint.__version__), \ f'pint version {pint.__version__} is too old: several numpy operations can cast away the unit' diff --git a/maintainer/benchmarks/p3m.py b/maintainer/benchmarks/p3m.py index c511376be9..beb9e75ea5 100644 --- a/maintainer/benchmarks/p3m.py +++ b/maintainer/benchmarks/p3m.py @@ -33,8 +33,10 @@ help="Fraction of the simulation box volume occupied by " "particles (range: [0.01-0.74], default: 0.25)") parser.add_argument("--prefactor", metavar="PREFACTOR", action="store", - type=float, default=4., required=False, + type=float, default=1., required=False, help="P3M prefactor (default: 4)") +parser.add_argument("--gpu", default=False, action="store_true") +parser.add_argument("--no-gpu", dest="gpu", action="store_false") group = parser.add_mutually_exclusive_group() group.add_argument("--output", metavar="FILEPATH", action="store", type=str, required=False, default="benchmarks.csv", @@ -55,7 +57,9 @@ assert measurement_steps >= 50, \ f"{measurement_steps} steps per tick are too short" -required_features = ["P3M", "LENNARD_JONES", "MASS"] +required_features = ["P3M", "LENNARD_JONES"] +if args.gpu: + required_features.append("CUDA") espressomd.assert_features(required_features) # make simulation deterministic @@ -67,7 +71,6 @@ # Interaction parameters (Lennard-Jones, Coulomb) ############################################################# - species = ["anion", "cation"] types = {"anion": 0, "cation": 0} charges = {"anion": -1.0, "cation": 1.0} @@ -76,12 +79,10 @@ WCA_cut = 2.**(1. / 6.) lj_cuts = {"anion": WCA_cut * lj_sigmas["anion"], "cation": WCA_cut * lj_sigmas["cation"]} -masses = {"anion": 1.0, "cation": 1.0} # System parameters ############################################################# - -n_proc = system.cell_system.get_state()['n_nodes'] +n_proc = system.cell_system.get_state()["n_nodes"] n_part = n_proc * args.particles_per_core # volume of N spheres with radius r: N * (4/3*pi*r^3) lj_sig = (lj_sigmas["cation"] + lj_sigmas["anion"]) / 2 @@ -96,12 +97,10 @@ # Integration parameters ############################################################# system.time_step = 0.01 -system.cell_system.skin = .4 -system.thermostat.turn_off() +system.cell_system.skin = 0.5 # Interaction setup ############################################################# - for i in range(len(species)): ion1 = species[i] for j in range(i, len(species)): @@ -115,26 +114,32 @@ # Particle setup ############################################################# - +pid = 0 for i in range(0, n_part, len(species)): for t in species: system.part.add(pos=np.random.random(3) * system.box_l, - q=charges[t], type=types[t], mass=masses[t]) + id=pid, q=charges[t], type=types[t]) + pid += 1 # Warmup Integration ############################################################# # warmup -benchmarks.minimize(system, n_part / 10.) +benchmarks.minimize(system, n_part / 2.) system.integrator.set_vv() system.thermostat.set_langevin(kT=1.0, gamma=1.0, seed=42) +p3m_class = espressomd.electrostatics.P3M +if args.gpu: + p3m_class = espressomd.electrostatics.P3MGPU + # tuning and equilibration min_skin = 0.2 max_skin = 1.6 -p3m_params = {'prefactor': args.prefactor, 'accuracy': 1e-4} -print("Equilibration") +p3m_params = {"prefactor": args.prefactor, "accuracy": 1e-3} +p3m = p3m_class(**p3m_params) +print("Quick equilibration") system.integrator.run(min(3 * measurement_steps, 1000)) print("Tune skin: {:.3f}".format(system.cell_system.tune_skin( min_skin=min_skin, max_skin=max_skin, tol=0.05, int_steps=100, @@ -142,7 +147,6 @@ print("Equilibration") system.integrator.run(min(3 * measurement_steps, 3000)) print("Tune p3m") -p3m = espressomd.electrostatics.P3M(**p3m_params) system.actors.add(p3m) print("Equilibration") system.integrator.run(min(3 * measurement_steps, 3000)) diff --git a/maintainer/gh_create_issue.sh b/maintainer/gh_create_issue.sh index 2fa1b1711c..0f1721df12 100755 --- a/maintainer/gh_create_issue.sh +++ b/maintainer/gh_create_issue.sh @@ -18,7 +18,7 @@ # along with this program. If not, see . # -URL="https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/pipelines/${CI_PIPELINE_ID}" +URL="https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/-/pipelines/${CI_PIPELINE_ID}" curl -s "https://api.github.com/repos/espressomd/espresso/issues" \ -H "Accept: application/vnd.github.full+json" \ diff --git a/maintainer/gh_post_status.sh b/maintainer/gh_post_status.sh index 40259b79e0..395136ef80 100755 --- a/maintainer/gh_post_status.sh +++ b/maintainer/gh_post_status.sh @@ -21,7 +21,7 @@ [ "${#}" -eq 1 ] || exit 1 GIT_COMMIT=$(git rev-parse HEAD) -URL="https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/pipelines/${CI_PIPELINE_ID}" +URL="https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/-/pipelines/${CI_PIPELINE_ID}" STATUS="${1}" curl "https://api.github.com/repos/espressomd/espresso/statuses/${GIT_COMMIT}" \ -H "Authorization: token ${GITHUB_TOKEN}" \ diff --git a/requirements.txt b/requirements.txt index a9f20773d1..d7fe018f2a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ +# build system +cython>=0.29.14,<3.0.10 # required scientific packages -numpy>=1.17.4 +numpy>=1.17.4,<2.0 h5py>=2.7.1 # optional scientific packages scipy>=1.4.0 @@ -11,8 +13,8 @@ vtk>=7.1.1 PyOpenGL>=3.1.0 pygame>=1.9.6 # CI-related -requests>=2.18.4 # to post on GitHub as espresso-ci -lxml>=4.2.1 # to deploy tutorials +requests>=2.32.0 +lxml>=4.2.1 # sphinx and its dependencies sphinx>=2.3.0,!=3.0.0 sphinx-toggleprompt==0.0.5 diff --git a/samples/h5md_trajectory.py b/samples/h5md_trajectory.py index 6d5f7b4a84..8cf8ba44ca 100644 --- a/samples/h5md_trajectory.py +++ b/samples/h5md_trajectory.py @@ -66,7 +66,8 @@ xyz_folded.append(system.part.all().pos_folded[:]) xyz_unfolded.append(system.part.all().pos[:]) # resize box (simulates NpT) -system.box_l = system.box_l + 1. +for i in range(3): + system.change_volume_and_rescale_particles(system.box_l[i] + 1., "xyz"[i]) system.integrator.run(10) h5.write() xyz_folded.append(system.part.all().pos_folded[:]) diff --git a/src/config/config.hpp b/src/config/config.hpp index 0363426466..02a74e38ad 100644 --- a/src/config/config.hpp +++ b/src/config/config.hpp @@ -54,7 +54,7 @@ #endif /** Whether to use the approximation of Abramowitz/Stegun @cite abramowitz65a - * @ref AS_erfc_part() for \f$\exp(d^2) \mathrm{erfc}(d)\f$, + * @ref Utils::AS_erfc_part() for \f$\exp(d^2) \mathrm{erfc}(d)\f$, * or the C function std::erfc() in P3M and Ewald summation. */ #ifndef USE_ERFC_APPROXIMATION diff --git a/src/core/BondList.hpp b/src/core/BondList.hpp index f199cc862a..96b78c4def 100644 --- a/src/core/BondList.hpp +++ b/src/core/BondList.hpp @@ -140,9 +140,9 @@ class BondList { auto const id_pos = find_end(m_it); auto const partners_begin = m_it; auto const partners_end = id_pos; - return {-(*id_pos) - 1, - Utils::make_span(std::addressof(*partners_begin), - std::distance(partners_begin, partners_end))}; + auto const dist = std::distance(partners_begin, partners_end); + return {-(*id_pos) - 1, Utils::make_span(std::addressof(*partners_begin), + static_cast(dist))}; } }; @@ -207,7 +207,9 @@ class BondList { * @brief Number of bonds. * @return The number of bonds in the list. */ - size_type size() const { return std::distance(begin(), end()); } + auto size() const { + return static_cast(std::distance(begin(), end())); + } /** * @brief Erase all bonds from the list. diff --git a/src/core/BoxGeometry.hpp b/src/core/BoxGeometry.hpp index 81503f9976..7224ec4a2b 100644 --- a/src/core/BoxGeometry.hpp +++ b/src/core/BoxGeometry.hpp @@ -187,7 +187,8 @@ class BoxGeometry { Utils::Vector get_mi_vector(const Utils::Vector &a, const Utils::Vector &b) const { if (type() == BoxType::LEES_EDWARDS) { - auto const shear_plane_normal = lees_edwards_bc().shear_plane_normal; + auto const shear_plane_normal = + static_cast(lees_edwards_bc().shear_plane_normal); auto a_tmp = a; auto b_tmp = b; a_tmp[shear_plane_normal] = Algorithm::periodic_fold( @@ -226,9 +227,13 @@ class BoxGeometry { auto ret = u - v; if (type() == BoxType::LEES_EDWARDS) { auto const &le = m_lees_edwards_bc; - auto const dy = x[le.shear_plane_normal] - y[le.shear_plane_normal]; - if (fabs(dy) > 0.5 * length_half()[le.shear_plane_normal]) { - ret[le.shear_direction] -= Utils::sgn(dy) * le.shear_velocity; + auto const shear_plane_normal = + static_cast(le.shear_plane_normal); + auto const shear_direction = + static_cast(le.shear_direction); + auto const dy = x[shear_plane_normal] - y[shear_plane_normal]; + if (fabs(dy) > 0.5 * length_half()[shear_plane_normal]) { + ret[shear_direction] -= Utils::sgn(dy) * le.shear_velocity; } } return ret; @@ -263,7 +268,7 @@ inline std::pair fold_coordinate(double pos, int image_box, */ inline void fold_position(Utils::Vector3d &pos, Utils::Vector3i &image_box, const BoxGeometry &box) { - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { if (box.periodic(i)) { std::tie(pos[i], image_box[i]) = fold_coordinate(pos[i], image_box[i], box.length()[i]); @@ -279,7 +284,7 @@ inline void fold_position(Utils::Vector3d &pos, Utils::Vector3i &image_box, inline Utils::Vector3d folded_position(const Utils::Vector3d &p, const BoxGeometry &box) { Utils::Vector3d p_folded; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { if (box.periodic(i)) { p_folded[i] = Algorithm::periodic_fold(p[i], box.length()[i]); } else { diff --git a/src/core/ComFixed.hpp b/src/core/ComFixed.hpp index a779f3aa4f..7bed1fe041 100644 --- a/src/core/ComFixed.hpp +++ b/src/core/ComFixed.hpp @@ -100,7 +100,7 @@ template class ComFixed { if (it != m_type_index.end()) { auto const mass_frac = p.mass() / masses[it->second]; auto const &type_force = forces[it->second]; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { p.force()[i] -= mass_frac * type_force[i]; } } diff --git a/src/core/EspressoSystemStandAlone.cpp b/src/core/EspressoSystemStandAlone.cpp index 90788c3c02..900fc5cdf1 100644 --- a/src/core/EspressoSystemStandAlone.cpp +++ b/src/core/EspressoSystemStandAlone.cpp @@ -29,17 +29,18 @@ #include #include +#include #include EspressoSystemStandAlone::EspressoSystemStandAlone(int argc, char **argv) { - auto mpi_env = mpi_init(argc, argv); + m_mpi_env = mpi_init(argc, argv); boost::mpi::communicator world; head_node = world.rank() == 0; // initialize the MpiCallbacks framework - Communication::init(mpi_env); + Communication::init(m_mpi_env); // default-construct global state of the system #ifdef VIRTUAL_SITES @@ -50,6 +51,10 @@ EspressoSystemStandAlone::EspressoSystemStandAlone(int argc, char **argv) { mpi_loop(); } +EspressoSystemStandAlone::~EspressoSystemStandAlone() { + Communication::deinit(); +} + void EspressoSystemStandAlone::set_box_l(Utils::Vector3d const &box_l) const { if (!head_node) return; diff --git a/src/core/EspressoSystemStandAlone.hpp b/src/core/EspressoSystemStandAlone.hpp index d118383565..198b235c25 100644 --- a/src/core/EspressoSystemStandAlone.hpp +++ b/src/core/EspressoSystemStandAlone.hpp @@ -21,12 +21,21 @@ #include +#include + +namespace boost { +namespace mpi { +class environment; +} +} // namespace boost + /** Manager for a stand-alone ESPResSo system. * The system is default-initialized, MPI-ready and has no script interface. */ class EspressoSystemStandAlone { public: EspressoSystemStandAlone(int argc, char **argv); + ~EspressoSystemStandAlone(); void set_box_l(Utils::Vector3d const &box_l) const; void set_node_grid(Utils::Vector3i const &node_grid) const; void set_time_step(double time_step) const; @@ -34,6 +43,7 @@ class EspressoSystemStandAlone { private: bool head_node; + std::shared_ptr m_mpi_env; }; #endif diff --git a/src/core/MpiCallbacks.hpp b/src/core/MpiCallbacks.hpp index 2f58f460d3..3c5e8f297c 100644 --- a/src/core/MpiCallbacks.hpp +++ b/src/core/MpiCallbacks.hpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -364,8 +365,8 @@ class MpiCallbacks { template ::argument_types, std::tuple>::value>> - CallbackHandle(MpiCallbacks *cb, F &&f) - : m_id(cb->add(std::forward(f))), m_cb(cb) {} + CallbackHandle(std::shared_ptr cb, F &&f) + : m_id(cb->add(std::forward(f))), m_cb(std::move(cb)) {} CallbackHandle(CallbackHandle const &) = delete; CallbackHandle(CallbackHandle &&rhs) noexcept = default; @@ -374,7 +375,7 @@ class MpiCallbacks { private: int m_id; - MpiCallbacks *m_cb; + std::shared_ptr m_cb; public: /** @@ -400,7 +401,6 @@ class MpiCallbacks { m_cb->remove(m_id); } - MpiCallbacks *cb() const { return m_cb; } int id() const { return m_id; } }; @@ -419,8 +419,10 @@ class MpiCallbacks { public: explicit MpiCallbacks(boost::mpi::communicator comm, + std::shared_ptr mpi_env, bool abort_on_exit = true) - : m_abort_on_exit(abort_on_exit), m_comm(std::move(comm)) { + : m_abort_on_exit(abort_on_exit), m_comm(std::move(comm)), + m_mpi_env(std::move(mpi_env)) { /* Add a dummy at id 0 for loop abort. */ m_callback_map.add(nullptr); @@ -721,6 +723,10 @@ class MpiCallbacks { */ boost::mpi::communicator const &comm() const { return m_comm; } + std::shared_ptr share_mpi_env() const { + return m_mpi_env; + } + private: /** * @brief Id for the @ref abort_loop. Has to be 0. @@ -738,6 +744,11 @@ class MpiCallbacks { */ boost::mpi::communicator m_comm; + /** + * The MPI environment used for the callbacks. + */ + std::shared_ptr m_mpi_env; + /** * Internal storage for the callback functions. */ diff --git a/src/core/Observable_stat.cpp b/src/core/Observable_stat.cpp index 96d9892b62..7a070b8833 100644 --- a/src/core/Observable_stat.cpp +++ b/src/core/Observable_stat.cpp @@ -37,7 +37,7 @@ #include #include -inline std::size_t max_non_bonded_pairs() { +static auto max_non_bonded_pairs() { auto const n = static_cast(max_seen_particle_type); return (n * (n + 1)) / 2; } @@ -52,13 +52,14 @@ Observable_stat::Observable_stat(std::size_t chunk_size) #else constexpr std::size_t n_vs = 0; #endif - auto const n_bonded = bonded_ia_params.get_next_key(); + auto const n_bonded = + static_cast(bonded_ia_params.get_next_key()); auto const n_non_bonded = max_non_bonded_pairs(); constexpr std::size_t n_ext_fields = 1; // reduction over all fields constexpr std::size_t n_kinetic = 1; // linear+angular kinetic contributions - auto const n_elements = n_kinetic + n_bonded + 2 * n_non_bonded + n_coulomb + - n_dipolar + n_vs + n_ext_fields; + auto const n_elements = n_kinetic + n_bonded + 2ul * n_non_bonded + + n_coulomb + n_dipolar + n_vs + n_ext_fields; m_data = std::vector(m_chunk_size * n_elements); // spans for the different contributions @@ -77,8 +78,8 @@ Observable_stat::Observable_stat(std::size_t chunk_size) } Utils::Span -Observable_stat::non_bonded_contribution(Utils::Span base_pointer, - int type1, int type2) const { +Observable_stat::get_non_bonded_contribution(Utils::Span base_pointer, + int type1, int type2) const { auto const offset = static_cast(Utils::upper_triangular( std::min(type1, type2), std::max(type1, type2), max_seen_particle_type)); return {base_pointer.begin() + offset * m_chunk_size, m_chunk_size}; diff --git a/src/core/Observable_stat.hpp b/src/core/Observable_stat.hpp index b34157004b..b84bcfdda0 100644 --- a/src/core/Observable_stat.hpp +++ b/src/core/Observable_stat.hpp @@ -38,8 +38,9 @@ class Observable_stat { std::size_t m_chunk_size; /** Get contribution from a non-bonded interaction */ - Utils::Span non_bonded_contribution(Utils::Span base_pointer, - int type1, int type2) const; + Utils::Span + get_non_bonded_contribution(Utils::Span base_pointer, int type1, + int type2) const; public: explicit Observable_stat(std::size_t chunk_size); @@ -91,29 +92,30 @@ class Observable_stat { return {bonded.data() + offset, m_chunk_size}; } - void add_non_bonded_contribution(int type1, int type2, + void add_non_bonded_contribution(int type1, int type2, int molid1, int molid2, Utils::Span data) { assert(data.size() == m_chunk_size); - auto const source = (type1 == type2) ? non_bonded_intra : non_bonded_inter; - auto const dest = non_bonded_contribution(source, type1, type2); + auto const span = (molid1 == molid2) ? non_bonded_intra : non_bonded_inter; + auto const dest = get_non_bonded_contribution(span, type1, type2); boost::transform(dest, data, dest.begin(), std::plus<>{}); } - void add_non_bonded_contribution(int type1, int type2, double data) { - add_non_bonded_contribution(type1, type2, {&data, 1}); + void add_non_bonded_contribution(int type1, int type2, int molid1, int molid2, + double data) { + add_non_bonded_contribution(type1, type2, molid1, molid2, {&data, 1}); } /** Get contribution from a non-bonded intramolecular interaction */ Utils::Span non_bonded_intra_contribution(int type1, int type2) const { - return non_bonded_contribution(non_bonded_intra, type1, type2); + return get_non_bonded_contribution(non_bonded_intra, type1, type2); } /** Get contribution from a non-bonded intermolecular interaction */ Utils::Span non_bonded_inter_contribution(int type1, int type2) const { - return non_bonded_contribution(non_bonded_inter, type1, type2); + return get_non_bonded_contribution(non_bonded_inter, type1, type2); } /** MPI reduction. */ diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 466ed17382..6e64fbba38 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -38,11 +38,9 @@ #include namespace detail { -inline void check_axis_idx_valid(int const axis) { - assert(axis >= 0 and axis <= 2); -} +inline void check_axis_idx_valid(unsigned int const axis) { assert(axis <= 2); } -inline bool get_nth_bit(uint8_t const bitfield, int const bit_idx) { +inline bool get_nth_bit(uint8_t const bitfield, unsigned int const bit_idx) { return bitfield & (1u << bit_idx); } } // namespace detail @@ -469,11 +467,11 @@ struct Particle { // NOLINT(bugprone-exception-escape) #ifdef ROTATION auto rotation() const { return p.rotation; } bool can_rotate() const { return static_cast(p.rotation); } - bool can_rotate_around(int const axis) const { + bool can_rotate_around(unsigned int const axis) const { detail::check_axis_idx_valid(axis); return detail::get_nth_bit(p.rotation, axis); } - void set_can_rotate_around(int const axis, bool const rot_flag) { + void set_can_rotate_around(unsigned int const axis, bool const rot_flag) { detail::check_axis_idx_valid(axis); if (rot_flag) { p.rotation |= static_cast(1u << axis); @@ -491,8 +489,10 @@ struct Particle { // NOLINT(bugprone-exception-escape) auto &torque() { return f.torque; } auto const &omega() const { return m.omega; } auto &omega() { return m.omega; } +#ifdef EXTERNAL_FORCES auto const &ext_torque() const { return p.ext_torque; } auto &ext_torque() { return p.ext_torque; } +#endif // EXTERNAL_FORCES auto calc_director() const { return r.calc_director(); } #else // ROTATION bool can_rotate() const { return false; } @@ -539,7 +539,7 @@ struct Particle { // NOLINT(bugprone-exception-escape) #endif // THERMOSTAT_PER_PARTICLE #ifdef EXTERNAL_FORCES bool has_fixed_coordinates() const { return static_cast(p.ext_flag); } - bool is_fixed_along(int const axis) const { + bool is_fixed_along(unsigned int const axis) const { detail::check_axis_idx_valid(axis); return detail::get_nth_bit(p.ext_flag, axis); } @@ -555,7 +555,7 @@ struct Particle { // NOLINT(bugprone-exception-escape) auto &ext_force() { return p.ext_force; } #else // EXTERNAL_FORCES constexpr bool has_fixed_coordinates() const { return false; } - constexpr bool is_fixed_along(int const) const { return false; } + constexpr bool is_fixed_along(unsigned int const) const { return false; } #endif // EXTERNAL_FORCES #ifdef ENGINE auto const &swimming() const { return p.swim; } diff --git a/src/core/cell_system/AtomDecomposition.hpp b/src/core/cell_system/AtomDecomposition.hpp index 8ce188d445..674b8972a7 100644 --- a/src/core/cell_system/AtomDecomposition.hpp +++ b/src/core/cell_system/AtomDecomposition.hpp @@ -117,7 +117,7 @@ class AtomDecomposition : public ParticleDecomposition { /** * @brief Get the local cell. */ - Cell &local() { return cells.at(m_comm.rank()); } + Cell &local() { return cells.at(static_cast(m_comm.rank())); } void configure_neighbors(); GhostCommunicator prepare_comm(); diff --git a/src/core/cell_system/CellStructure.hpp b/src/core/cell_system/CellStructure.hpp index f4a47b29f7..01d0ee04e7 100644 --- a/src/core/cell_system/CellStructure.hpp +++ b/src/core/cell_system/CellStructure.hpp @@ -159,10 +159,10 @@ struct CellStructure { // cppcheck-suppress assertWithSideEffect assert(not p or p->id() == id); - if (id >= m_particle_index.size()) - m_particle_index.resize(id + 1); + if (static_cast(id) >= m_particle_index.size()) + m_particle_index.resize(static_cast(id + 1)); - m_particle_index[id] = p; + m_particle_index[static_cast(id)] = p; } /** @@ -227,20 +227,20 @@ struct CellStructure { Particle *get_local_particle(int id) { assert(id >= 0); - if (id >= m_particle_index.size()) + if (static_cast(id) >= m_particle_index.size()) return nullptr; - return m_particle_index[id]; + return m_particle_index[static_cast(id)]; } /** @overload */ const Particle *get_local_particle(int id) const { assert(id >= 0); - if (id >= m_particle_index.size()) + if (static_cast(id) >= m_particle_index.size()) return nullptr; - return m_particle_index[id]; + return m_particle_index[static_cast(id)]; } template diff --git a/src/core/cell_system/RegularDecomposition.cpp b/src/core/cell_system/RegularDecomposition.cpp index b805a560d9..abce3f0cbb 100644 --- a/src/core/cell_system/RegularDecomposition.cpp +++ b/src/core/cell_system/RegularDecomposition.cpp @@ -53,7 +53,7 @@ Cell *RegularDecomposition::position_to_cell(const Utils::Vector3d &pos) { Utils::Vector3i cpos; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { cpos[i] = static_cast(std::floor(pos[i] * inv_cell_size[i])) + 1 - cell_offset[i]; @@ -266,8 +266,9 @@ void RegularDecomposition::fill_comm_cell_lists(ParticleList **part_lists, *part_lists++ = &(cells.at(i).particles()); } } + Utils::Vector3d RegularDecomposition::max_cutoff() const { - auto dir_max_range = [this](int i) { + auto dir_max_range = [this](unsigned int i) { return std::min(0.5 * m_box.length()[i], m_local_box.length()[i]); }; @@ -307,7 +308,7 @@ void RegularDecomposition::create_cell_grid(double range) { auto const volume = Utils::product(local_box_l); auto const scale = std::cbrt(RegularDecomposition::max_num_cells / volume); - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { /* this is at least 1 */ cell_grid[i] = static_cast(std::ceil(local_box_l[i] * scale)); cell_range[i] = local_box_l[i] / static_cast(cell_grid[i]); @@ -370,7 +371,7 @@ void RegularDecomposition::create_cell_grid(double range) { /* now set all dependent variables */ int new_cells = 1; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { ghost_cell_grid[i] = cell_grid[i] + 2; new_cells *= ghost_cell_grid[i]; cell_size[i] = m_local_box.length()[i] / static_cast(cell_grid[i]); @@ -380,7 +381,7 @@ void RegularDecomposition::create_cell_grid(double range) { /* allocate cell array and cell pointer arrays */ cells.clear(); - cells.resize(new_cells); + cells.resize(static_cast(new_cells)); m_local_cells.resize(n_local_cells); m_ghost_cells.resize(new_cells - n_local_cells); } diff --git a/src/core/collision.cpp b/src/core/collision.cpp index 7f3524d356..488c71558f 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -95,7 +95,7 @@ static bool bind_centers() { // but does so later in the process. This is needed to guarantee that // a particle can only be glued once, even if queued twice in a single // time step - return collision_params.mode != CollisionModeType::OFF && + return collision_params.mode != CollisionModeType::OFF and collision_params.mode != CollisionModeType::GLUE_TO_SURF; } @@ -132,7 +132,7 @@ void Collision_parameters::initialize() { #ifdef VIRTUAL_SITES // Check vs placement parameter if (collision_params.mode == CollisionModeType::BIND_VS) { - if ((collision_params.vs_placement < 0.) || + if ((collision_params.vs_placement < 0.) or (collision_params.vs_placement > 1.)) { throw std::domain_error( "Parameter 'vs_placement' must be between 0 and 1"); @@ -141,13 +141,13 @@ void Collision_parameters::initialize() { #endif // Check if bonded ia exist - if ((collision_params.mode == CollisionModeType::BIND_CENTERS) && + if ((collision_params.mode == CollisionModeType::BIND_CENTERS) and !bonded_ia_params.contains(collision_params.bond_centers)) { throw std::runtime_error( "Bond in parameter 'bond_centers' was not added to the system"); } - if ((collision_params.mode == CollisionModeType::BIND_VS) && + if ((collision_params.mode == CollisionModeType::BIND_VS) and !bonded_ia_params.contains(collision_params.bond_vs)) { throw std::runtime_error( "Bond in parameter 'bond_vs' was not added to the system"); @@ -155,16 +155,16 @@ void Collision_parameters::initialize() { // If the bond type to bind particle centers is not a pair bond... // Check that the bonds have the right number of partners - if ((collision_params.mode == CollisionModeType::BIND_CENTERS) && + if ((collision_params.mode == CollisionModeType::BIND_CENTERS) and (get_bond_num_partners(collision_params.bond_centers) != 1)) { throw std::runtime_error("The bond type to be used for binding particle " "centers needs to be a pair bond"); } // The bond between the virtual sites can be pair or triple - if ((collision_params.mode == CollisionModeType::BIND_VS) && - !(get_bond_num_partners(collision_params.bond_vs) == 1 || - get_bond_num_partners(collision_params.bond_vs) == 2)) { + if ((collision_params.mode == CollisionModeType::BIND_VS) and + (get_bond_num_partners(collision_params.bond_vs) != 1 and + get_bond_num_partners(collision_params.bond_vs) != 2)) { throw std::runtime_error("The bond type to be used for binding virtual " "sites needs to be a pair or three-particle bond"); } @@ -249,10 +249,10 @@ const Particle &glue_to_surface_calc_vs_pos(const Particle &p1, const double dist_betw_part = vec21.norm(); // Find out, which is the particle to be glued. - if ((p1.type() == collision_params.part_type_to_be_glued) && + if ((p1.type() == collision_params.part_type_to_be_glued) and (p2.type() == collision_params.part_type_to_attach_vs_to)) { c = 1 - collision_params.dist_glued_part_to_vs / dist_betw_part; - } else if ((p2.type() == collision_params.part_type_to_be_glued) && + } else if ((p2.type() == collision_params.part_type_to_be_glued) and (p1.type() == collision_params.part_type_to_attach_vs_to)) { c = collision_params.dist_glued_part_to_vs / dist_betw_part; } else { @@ -306,8 +306,8 @@ void coldet_do_three_particle_bond(Particle &p, Particle const &p1, id2 = p2.id()](BondView const &bond) { auto const partner_ids = bond.partner_ids(); - return ((partner_ids[0] == id1) && (partner_ids[1] == id2)) || - ((partner_ids[0] == id2) && (partner_ids[1] == id1)); + return ((partner_ids[0] == id1) and (partner_ids[1] == id2)) or + ((partner_ids[0] == id2) and (partner_ids[1] == id1)); }; if (boost::algorithm::any_of(p.bonds(), [=](auto const &bond) { @@ -459,7 +459,7 @@ void three_particle_binding_domain_decomposition( for (auto &c : gathered_queue) { // If we have both particles, at least as ghosts, Get the corresponding cell // indices - if (cell_structure.get_local_particle(c.pp1) && + if (cell_structure.get_local_particle(c.pp1) and cell_structure.get_local_particle(c.pp2)) { Particle &p1 = *cell_structure.get_local_particle(c.pp1); Particle &p2 = *cell_structure.get_local_particle(c.pp2); @@ -468,7 +468,7 @@ void three_particle_binding_domain_decomposition( if (cell1) three_particle_binding_do_search(cell1, p1, p2); - if (cell2 && cell1 != cell2) + if (cell2 and cell1 != cell2) three_particle_binding_do_search(cell2, p1, p2); } // If local particles exist diff --git a/src/core/communication.cpp b/src/core/communication.cpp index 20b7d05263..2268e88f4d 100644 --- a/src/core/communication.cpp +++ b/src/core/communication.cpp @@ -28,6 +28,8 @@ #include #include +#include +#include #include #ifdef OPEN_MPI @@ -39,15 +41,10 @@ #include #include -namespace Communication { -auto const &mpi_datatype_cache = boost::mpi::detail::mpi_datatype_cache(); -std::shared_ptr mpi_env; -} // namespace Communication - boost::mpi::communicator comm_cart; namespace Communication { -std::unique_ptr m_callbacks; +static std::shared_ptr m_callbacks; /* We use a singleton callback class for now. */ MpiCallbacks &mpiCallbacks() { @@ -55,6 +52,12 @@ MpiCallbacks &mpiCallbacks() { return *m_callbacks; } + +std::shared_ptr mpiCallbacksHandle() { + assert(m_callbacks && "Mpi not initialized!"); + + return m_callbacks; +} } // namespace Communication using Communication::mpiCallbacks; @@ -120,8 +123,6 @@ void openmpi_global_namespace() { namespace Communication { void init(std::shared_ptr mpi_env) { - Communication::mpi_env = std::move(mpi_env); - MPI_Comm_size(MPI_COMM_WORLD, &n_nodes); node_grid = Utils::Mpi::dims_create<3>(n_nodes); @@ -131,12 +132,14 @@ void init(std::shared_ptr mpi_env) { this_node = comm_cart.rank(); Communication::m_callbacks = - std::make_unique(comm_cart); + std::make_shared(comm_cart, mpi_env); - ErrorHandling::init_error_handling(mpiCallbacks()); + ErrorHandling::init_error_handling(Communication::m_callbacks); on_program_start(); } + +void deinit() { Communication::m_callbacks.reset(); } } // namespace Communication std::shared_ptr mpi_init(int argc, char **argv) { diff --git a/src/core/communication.hpp b/src/core/communication.hpp index cab2d8507a..4bc11ae8ac 100644 --- a/src/core/communication.hpp +++ b/src/core/communication.hpp @@ -63,6 +63,7 @@ namespace Communication { * @brief Returns a reference to the global callback class instance. */ MpiCallbacks &mpiCallbacks(); +std::shared_ptr mpiCallbacksHandle(); } // namespace Communication /************************************************** @@ -136,12 +137,9 @@ namespace Communication { /** * @brief Init globals for communication. * - * and calls @ref on_program_start. Keeps a copy of - * the pointer to the mpi environment to keep it alive - * while the program is loaded. - * * @param mpi_env MPI environment that should be used */ void init(std::shared_ptr mpi_env); +void deinit(); } // namespace Communication #endif diff --git a/src/core/constraints/Constraints.hpp b/src/core/constraints/Constraints.hpp index bfc9ca9317..49cf01be15 100644 --- a/src/core/constraints/Constraints.hpp +++ b/src/core/constraints/Constraints.hpp @@ -101,12 +101,14 @@ template class Constraints { } } - void on_boxl_change() const { - if (not this->empty()) { + void veto_boxl_change() const { + if (not m_constraints.empty()) { throw std::runtime_error("The box size can not be changed because there " "are active constraints."); } } + + void on_boxl_change() const { veto_boxl_change(); } }; } // namespace Constraints diff --git a/src/core/constraints/ShapeBasedConstraint.cpp b/src/core/constraints/ShapeBasedConstraint.cpp index 50e85159c3..8d59596927 100644 --- a/src/core/constraints/ShapeBasedConstraint.cpp +++ b/src/core/constraints/ShapeBasedConstraint.cpp @@ -154,7 +154,10 @@ void ShapeBasedConstraint::add_energy(const Particle &p, runtimeErrorMsg() << "Constraint violated by particle " << p.id(); } } - if (part_rep.type() >= 0) - obs_energy.add_non_bonded_contribution(p.type(), part_rep.type(), energy); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + if (part_rep.type() >= 0) { + obs_energy.add_non_bonded_contribution( + p.type(), part_rep.type(), p.mol_id(), part_rep.mol_id(), energy); + } } } // namespace Constraints diff --git a/src/core/cuda_init.cpp b/src/core/cuda_init.cpp index 64ff562b6f..ae6c3733a8 100644 --- a/src/core/cuda_init.cpp +++ b/src/core/cuda_init.cpp @@ -91,7 +91,7 @@ static std::vector mpi_cuda_gather_gpus_local() { if (this_node == 0) { std::set device_set; - int *n_gpu_array = new int[n_nodes]; + int *n_gpu_array = new int[static_cast(n_nodes)]; MPI_Gather(&n_gpus, 1, MPI_INT, n_gpu_array, 1, MPI_INT, 0, MPI_COMM_WORLD); /* insert local devices */ diff --git a/src/core/cuda_init.hpp b/src/core/cuda_init.hpp index 3e9d10eca6..3d6c12a95b 100644 --- a/src/core/cuda_init.hpp +++ b/src/core/cuda_init.hpp @@ -34,7 +34,7 @@ struct EspressoGpuDevice { /** Local CUDA device id */ int id; /** Local CUDA device name */ - char name[64]; + char name[256]; /** Node identification */ char proc_name[64]; /** MPI process identification */ @@ -71,9 +71,9 @@ int cuda_check_gpu_compute_capability(int dev); /** Get the name of a CUDA device. * * @param[in] dev the CUDA device number to ask the name for - * @param[out] name a buffer to write the name to, at least 64 characters + * @param[out] name a buffer to write the name to, at least 256 characters */ -void cuda_get_gpu_name(int dev, char name[64]); +void cuda_get_gpu_name(int dev, char *name); /** Choose a device for future CUDA computations. * diff --git a/src/core/cuda_init_cuda.cu b/src/core/cuda_init_cuda.cu index f1d8f608fe..8f9edb8e78 100644 --- a/src/core/cuda_init_cuda.cu +++ b/src/core/cuda_init_cuda.cu @@ -58,11 +58,20 @@ int cuda_check_gpu_compute_capability(int dev) { return ES_OK; } -void cuda_get_gpu_name(int dev, char name[64]) { +/** + * @brief Safely copy the device name and pad the string with null characters. + */ +static void cuda_copy_gpu_name(char *const name, cudaDeviceProp const &prop) { + char buffer[256] = {'\0'}; + std::strncpy(buffer, prop.name, 256); + name[255] = '\0'; + std::strncpy(name, buffer, 256); +} + +void cuda_get_gpu_name(int dev, char *const name) { cudaDeviceProp deviceProp; CUDA_CHECK(cudaGetDeviceProperties(&deviceProp, dev)) - std::strncpy(name, deviceProp.name, 63); - name[63] = 0; + cuda_copy_gpu_name(name, deviceProp); } EspressoGpuDevice cuda_get_device_props(const int dev) { @@ -76,8 +85,7 @@ EspressoGpuDevice cuda_get_device_props(const int dev) { deviceProp.minor, deviceProp.totalGlobalMem, deviceProp.multiProcessorCount}; - std::strncpy(device.name, deviceProp.name, 64); - device.name[63] = '\0'; + cuda_copy_gpu_name(device.name, deviceProp); return device; } diff --git a/src/core/cuda_interface.cpp b/src/core/cuda_interface.cpp index 221bfb2fe0..3f08e6d95f 100644 --- a/src/core/cuda_interface.cpp +++ b/src/core/cuda_interface.cpp @@ -115,12 +115,12 @@ void cuda_mpi_get_particles( static void add_forces_and_torques(ParticleRange particles, Utils::Span forces, Utils::Span torques) { - int i = 0; + unsigned int i = 0; for (auto &p : particles) { - for (int j = 0; j < 3; j++) { - p.force()[j] += forces[3 * i + j]; + for (unsigned int j = 0; j < 3; j++) { + p.force()[j] += static_cast(forces[3 * i + j]); #ifdef ROTATION - p.torque()[j] += torques[3 * i + j]; + p.torque()[j] += static_cast(torques[3 * i + j]); #endif } i++; diff --git a/src/core/electrostatics/p3m.cpp b/src/core/electrostatics/p3m.cpp index 69c6eb4ed3..f959c411e8 100644 --- a/src/core/electrostatics/p3m.cpp +++ b/src/core/electrostatics/p3m.cpp @@ -280,7 +280,7 @@ CoulombP3M::CoulombP3M(P3MParameters &¶meters, double prefactor, } namespace { -template struct AssignCharge { +template struct AssignCharge { void operator()(p3m_data_struct &p3m, double q, Utils::Vector3d const &real_pos, p3m_interpolation_cache &inter_weights) { @@ -320,22 +320,23 @@ void CoulombP3M::charge_assign(ParticleRange const &particles) { for (int i = 0; i < p3m.local_mesh.size; i++) p3m.rs_mesh[i] = 0.0; - Utils::integral_parameter(p3m.params.cao, p3m, particles); + Utils::integral_parameter(p3m.params.cao, p3m, + particles); } void CoulombP3M::assign_charge(double q, Utils::Vector3d const &real_pos, p3m_interpolation_cache &inter_weights) { - Utils::integral_parameter(p3m.params.cao, p3m, q, - real_pos, inter_weights); + Utils::integral_parameter(p3m.params.cao, p3m, q, + real_pos, inter_weights); } void CoulombP3M::assign_charge(double q, Utils::Vector3d const &real_pos) { - Utils::integral_parameter(p3m.params.cao, p3m, q, - real_pos); + Utils::integral_parameter(p3m.params.cao, p3m, q, + real_pos); } namespace { -template struct AssignForces { +template struct AssignForces { void operator()(p3m_data_struct &p3m, double force_prefac, ParticleRange const &particles) const { using Utils::make_const_span; @@ -502,8 +503,8 @@ double CoulombP3M::long_range_kernel(bool force_flag, bool energy_flag, p3m.local_mesh.dim); auto const force_prefac = prefactor / volume; - Utils::integral_parameter(p3m.params.cao, p3m, - force_prefac, particles); + Utils::integral_parameter(p3m.params.cao, p3m, + force_prefac, particles); // add dipole forces if (p3m.params.epsilon != P3M_EPSILON_METALLIC) { @@ -728,7 +729,7 @@ void CoulombP3M::tune() { } void CoulombP3M::sanity_checks_boxl() const { - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { /* check k-space cutoff */ if (p3m.params.cao_cut[i] >= box_geo.length_half()[i]) { std::stringstream msg; @@ -745,10 +746,10 @@ void CoulombP3M::sanity_checks_boxl() const { } if (p3m.params.epsilon != P3M_EPSILON_METALLIC) { - if (!((box_geo.length()[0] == box_geo.length()[1]) and - (box_geo.length()[1] == box_geo.length()[2])) or - !((p3m.params.mesh[0] == p3m.params.mesh[1]) and - (p3m.params.mesh[1] == p3m.params.mesh[2]))) { + if ((box_geo.length()[0] != box_geo.length()[1]) or + (box_geo.length()[1] != box_geo.length()[2]) or + (p3m.params.mesh[0] != p3m.params.mesh[1]) or + (p3m.params.mesh[1] != p3m.params.mesh[2])) { throw std::runtime_error( "CoulombP3M: non-metallic epsilon requires cubic box"); } diff --git a/src/core/electrostatics/p3m_gpu_error_cuda.cu b/src/core/electrostatics/p3m_gpu_error_cuda.cu index 047efbe59e..3419631871 100644 --- a/src/core/electrostatics/p3m_gpu_error_cuda.cu +++ b/src/core/electrostatics/p3m_gpu_error_cuda.cu @@ -120,182 +120,6 @@ __global__ void p3m_k_space_error_gpu_kernel_ik(int3 mesh, double3 meshi, } } -__global__ void p3m_k_space_error_gpu_kernel_ad(const int3 mesh, - const double3 meshi, int cao, - double alpha_L, double *he_q) { - auto const nx = - -mesh.x / 2 + static_cast(blockDim.x * blockIdx.x + threadIdx.x); - auto const ny = - -mesh.y / 2 + static_cast(blockDim.y * blockIdx.y + threadIdx.y); - auto const nz = - -mesh.z / 2 + static_cast(blockDim.z * blockIdx.z + threadIdx.z); - - if ((nx >= mesh.x / 2) || (ny >= mesh.y / 2) || (nz >= mesh.z / 2)) - return; - - int lind = ((nx + mesh.x / 2) * mesh.y * mesh.z + (ny + mesh.y / 2) * mesh.z + - (nz + mesh.z / 2)); - - auto const alpha_L_i = 1. / alpha_L; - double alias1, alias2, alias3, alias4; - alias1 = alias2 = alias3 = alias4 = 0; - - if ((nx != 0) || (ny != 0) || (nz != 0)) { - for (int mx = -1; mx <= 1; mx++) { - auto const nmx = static_cast(nx + mx * mesh.x); - for (int my = -1; my <= 1; my++) { - auto const nmy = static_cast(ny + my * mesh.y); - for (int mz = -1; mz <= 1; mz++) { - auto const nmz = static_cast(nz + mz * mesh.z); - - auto const n2 = static_cast(sqr(nmx) + sqr(nmy) + sqr(nmz)); - auto const ex = exp(-sqr(Utils::pi() * alpha_L_i) * n2); - auto const ex2 = sqr(ex); - auto const U2 = - pow(Utils::sinc(meshi.x * nmx) * Utils::sinc(meshi.y * nmy) * - Utils::sinc(meshi.z * nmz), - 2.0 * cao); - - alias1 += ex2 / n2; - alias2 += U2 * ex; - alias3 += U2 * n2; - alias4 += U2; - } - } - } - - if ((alias3 == 0.0) || (alias4 == 0.0)) - he_q[lind] = 0; - else - he_q[lind] = alias1 - (alias2 * alias2) / (alias3 * alias4); - - } else { - he_q[lind] = 0; - } -} - -__global__ void p3m_k_space_error_gpu_kernel_ik_i(const int3 mesh, - const double3 meshi, int cao, - double alpha_L, - double *he_q) { - - auto const nx = - -mesh.x / 2 + static_cast(blockDim.x * blockIdx.x + threadIdx.x); - auto const ny = - -mesh.y / 2 + static_cast(blockDim.y * blockIdx.y + threadIdx.y); - auto const nz = - -mesh.z / 2 + static_cast(blockDim.z * blockIdx.z + threadIdx.z); - - if ((nx >= mesh.x / 2) || (ny >= mesh.y / 2) || (nz >= mesh.z / 2)) - return; - - int lind = ((nx + mesh.x / 2) * mesh.y * mesh.z + (ny + mesh.y / 2) * mesh.z + - (nz + mesh.z / 2)); - - auto const alpha_L_i = 1. / alpha_L; - double alias1, alias2, alias3, alias4; - alias1 = alias2 = alias3 = alias4 = 0; - - if ((nx != 0) || (ny != 0) || (nz != 0)) { - for (int mx = -1; mx <= 1; mx++) { - auto const nmx = static_cast(nx + mx * mesh.x); - for (int my = -1; my <= 1; my++) { - auto const nmy = static_cast(ny + my * mesh.y); - for (int mz = -1; mz <= 1; mz++) { - auto const nmz = static_cast(nz + mz * mesh.z); - - auto const n2 = static_cast(sqr(nmx) + sqr(nmy) + sqr(nmz)); - auto const ex = exp(-sqr(Utils::pi() * alpha_L_i) * n2); - auto const ex2 = sqr(ex); - auto const U2 = - pow(Utils::sinc(meshi.x * nmx) * Utils::sinc(meshi.y * nmy) * - Utils::sinc(meshi.z * nmz), - 2.0 * cao); - - alias1 += ex2 / n2; - alias2 += U2 * ex * (nx * nmx + ny * nmy + nz * nmz) / n2; - alias3 += U2; - - if (((mx + my + mz) % 2) == 0) { // consider only even terms! - alias4 += U2; - } else { - alias4 -= U2; - } - } - } - } - - he_q[lind] = - alias1 - (alias2 * alias2) / (0.5 * (nx * nx + ny * ny + nz * nz) * - (alias3 * alias3 + alias4 * alias4)); - - } else { - he_q[lind] = 0; - } -} - -__global__ void p3m_k_space_error_gpu_kernel_ad_i(const int3 mesh, - const double3 meshi, int cao, - double alpha_L, - double *he_q) { - - auto const nx = - -mesh.x / 2 + static_cast(blockDim.x * blockIdx.x + threadIdx.x); - auto const ny = - -mesh.y / 2 + static_cast(blockDim.y * blockIdx.y + threadIdx.y); - auto const nz = - -mesh.z / 2 + static_cast(blockDim.z * blockIdx.z + threadIdx.z); - - if ((nx >= mesh.x / 2) || (ny >= mesh.y / 2) || (nz >= mesh.z / 2)) - return; - - int lind = ((nx + mesh.x / 2) * mesh.y * mesh.z + (ny + mesh.y / 2) * mesh.z + - (nz + mesh.z / 2)); - - auto const alpha_L_i = 1. / alpha_L; - double alias1, alias2, alias3, alias4, alias5, alias6; - alias1 = alias2 = alias3 = alias4 = alias5 = alias6 = 0; - - if ((nx != 0) && (ny != 0) && (nz != 0)) { - for (int mx = -1; mx <= 1; mx++) { - auto const nmx = static_cast(nx + mx * mesh.x); - for (int my = -1; my <= 1; my++) { - auto const nmy = static_cast(ny + my * mesh.y); - for (int mz = -1; mz <= 1; mz++) { - auto const nmz = static_cast(nz + mz * mesh.z); - - auto const n2 = sqr(nmx) + sqr(nmy) + sqr(nmz); - auto const ex = exp(-sqr(Utils::pi() * alpha_L_i) * n2); - auto const ex2 = sqr(ex); - auto const U2 = - pow(Utils::sinc(meshi.x * nmx) * Utils::sinc(meshi.y * nmy) * - Utils::sinc(meshi.z * nmz), - 2.0 * cao); - - alias1 += ex2 / n2; - alias2 += U2 * ex; - alias3 += U2 * n2; - alias4 += U2; - - if (((mx + my + mz) % 2) == 0) { // even term - alias5 += U2 * n2; - alias6 += U2; - } else { // odd term: minus sign! - alias5 -= U2 * n2; - alias6 -= U2; - } - } - } - } - - he_q[lind] = - (alias1 - sqr(alias2) / (0.5 * (alias3 * alias4 + alias5 * alias6))); - - } else { - he_q[lind] = 0; - } -} - double p3m_k_space_error_gpu(double prefactor, const int *mesh, int cao, int npart, double sum_q2, double alpha_L, const double *box) { diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index ba6d36b77a..3215ddbc64 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -184,7 +184,7 @@ inline void add_non_bonded_pair_energy( if (do_nonbonded(p1, p2)) #endif obs_energy.add_non_bonded_contribution( - p1.type(), p2.type(), + p1.type(), p2.type(), p1.mol_id(), p2.mol_id(), calc_non_bonded_pair_energy(p1, p2, ia_params, d, dist, coulomb_kernel)); @@ -300,7 +300,7 @@ inline double translational_kinetic_energy(Particle const &p) { */ inline double rotational_kinetic_energy(Particle const &p) { #ifdef ROTATION - return p.can_rotate() + return (p.can_rotate() and not p.is_virtual()) ? 0.5 * (hadamard_product(p.omega(), p.omega()) * p.rinertia()) : 0.0; #else diff --git a/src/core/errorhandling.cpp b/src/core/errorhandling.cpp index 6a50495282..be74ef73cd 100644 --- a/src/core/errorhandling.cpp +++ b/src/core/errorhandling.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include namespace ErrorHandling { @@ -45,14 +46,14 @@ namespace { std::unique_ptr runtimeErrorCollector; /** The callback loop we are on. */ -Communication::MpiCallbacks *m_callbacks = nullptr; +std::weak_ptr m_callbacks; } // namespace -void init_error_handling(Communication::MpiCallbacks &cb) { - m_callbacks = &cb; +void init_error_handling(std::weak_ptr callbacks) { + m_callbacks = std::move(callbacks); runtimeErrorCollector = - std::make_unique(m_callbacks->comm()); + std::make_unique(m_callbacks.lock()->comm()); } RuntimeErrorStream _runtimeMessageStream(RuntimeError::ErrorLevel level, @@ -69,7 +70,7 @@ static void mpi_gather_runtime_errors_local() { REGISTER_CALLBACK(mpi_gather_runtime_errors_local) std::vector mpi_gather_runtime_errors() { - m_callbacks->call(mpi_gather_runtime_errors_local); + m_callbacks.lock()->call(mpi_gather_runtime_errors_local); return runtimeErrorCollector->gather(); } @@ -83,7 +84,7 @@ std::vector mpi_gather_runtime_errors_all(bool is_head_node) { } // namespace ErrorHandling void errexit() { - ErrorHandling::m_callbacks->comm().abort(1); + ErrorHandling::m_callbacks.lock()->comm().abort(1); std::abort(); } diff --git a/src/core/errorhandling.hpp b/src/core/errorhandling.hpp index 1d79a1a94e..66a4af536c 100644 --- a/src/core/errorhandling.hpp +++ b/src/core/errorhandling.hpp @@ -31,6 +31,7 @@ #include "error_handling/RuntimeError.hpp" #include "error_handling/RuntimeErrorStream.hpp" +#include #include #include @@ -85,7 +86,7 @@ namespace ErrorHandling { * * @param callbacks Callbacks system the error handler should be on. */ -void init_error_handling(Communication::MpiCallbacks &callbacks); +void init_error_handling(std::weak_ptr callbacks); RuntimeErrorStream _runtimeMessageStream(RuntimeError::ErrorLevel level, const std::string &file, int line, diff --git a/src/core/event.cpp b/src/core/event.cpp index 986c3fcad5..41c6a072f1 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -31,6 +31,7 @@ #include "collision.hpp" #include "communication.hpp" #include "config.hpp" +#include "constraints.hpp" #include "cuda_init.hpp" #include "cuda_interface.hpp" #include "cuda_utils.hpp" @@ -268,6 +269,16 @@ void on_lbboundary_change() { #endif } +void veto_boxl_change(bool skip_particle_checks) { + if (not skip_particle_checks) { + if (get_n_part() > 0) { + throw std::runtime_error( + "Cannot reset the box length when particles are present"); + } + } + Constraints::constraints.veto_boxl_change(); +} + void on_boxl_change(bool skip_method_adaption) { grid_changed_box_l(box_geo); /* Electrostatics cutoffs mostly depend on the system size, @@ -289,6 +300,8 @@ void on_boxl_change(bool skip_method_adaption) { LBBoundaries::lb_init_boundaries(); #endif } + + Constraints::constraints.on_boxl_change(); } void on_cell_structure_change() { diff --git a/src/core/event.hpp b/src/core/event.hpp index 6e39cbff4a..912eac1777 100644 --- a/src/core/event.hpp +++ b/src/core/event.hpp @@ -82,6 +82,13 @@ void on_short_range_ia_change(); /** called every time a constraint is changed. */ void on_constraint_change(); +/** + * @brief Called before the box length is changed. + * + * @param skip_particle_checks skip the particle checks + */ +void veto_boxl_change(bool skip_particle_checks); + /** * @brief Called when the box length has changed. This routine is relatively * fast, and changing the box length every time step as for example necessary diff --git a/src/core/ghosts.cpp b/src/core/ghosts.cpp index b62d4bf994..f9f5da5af9 100644 --- a/src/core/ghosts.cpp +++ b/src/core/ghosts.cpp @@ -107,10 +107,10 @@ static std::size_t calc_transmit_size(unsigned data_parts) { static std::size_t calc_transmit_size(const GhostCommunication &ghost_comm, unsigned int data_parts) { if (data_parts & GHOSTTRANS_PARTNUM) - return sizeof(int) * ghost_comm.part_lists.size(); + return sizeof(unsigned int) * ghost_comm.part_lists.size(); auto const n_part = boost::accumulate( - ghost_comm.part_lists, 0ul, + ghost_comm.part_lists, std::size_t{0}, [](std::size_t sum, auto part_list) { return sum + part_list->size(); }); return n_part * calc_transmit_size(data_parts); @@ -134,7 +134,8 @@ static void prepare_send_buffer(CommBuf &send_buffer, /* put in data */ for (auto part_list : ghost_comm.part_lists) { if (data_parts & GHOSTTRANS_PARTNUM) { - int np = static_cast(part_list->size()); + assert(part_list->size() <= std::numeric_limits::max()); + auto np = static_cast(part_list->size()); archiver << np; } else { for (Particle &part : *part_list) { @@ -169,7 +170,7 @@ static void prepare_send_buffer(CommBuf &send_buffer, assert(archiver.bytes_written() == send_buffer.size()); } -static void prepare_ghost_cell(ParticleList *cell, int size) { +static void prepare_ghost_cell(ParticleList *cell, std::size_t size) { /* Adapt size */ cell->resize(size); @@ -196,7 +197,7 @@ static void put_recv_buffer(CommBuf &recv_buffer, if (data_parts & GHOSTTRANS_PARTNUM) { for (auto part_list : ghost_comm.part_lists) { - int np; + unsigned int np; archiver >> np; prepare_ghost_cell(part_list, np); } @@ -279,7 +280,7 @@ static void cell_cell_transfer(const GhostCommunication &ghost_comm, auto *dst_list = ghost_comm.part_lists[pl + offset]; if (data_parts & GHOSTTRANS_PARTNUM) { - prepare_ghost_cell(dst_list, static_cast(src_list->size())); + prepare_ghost_cell(dst_list, src_list->size()); } else { auto const &src_part = *src_list; auto &dst_part = *dst_list; diff --git a/src/core/grid.cpp b/src/core/grid.cpp index f4e7e8e1e5..521f02dde2 100644 --- a/src/core/grid.cpp +++ b/src/core/grid.cpp @@ -27,6 +27,7 @@ #include "grid.hpp" #include "communication.hpp" +#include "errorhandling.hpp" #include "event.hpp" #include "particle_data.hpp" @@ -38,7 +39,7 @@ #include #include -#include +#include BoxGeometry box_geo; LocalBox local_geo; @@ -51,7 +52,7 @@ int map_position_node_array(const Utils::Vector3d &pos) { auto const f_pos = folded_position(pos, box_geo); Utils::Vector3i im; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { im[i] = static_cast(std::floor(f_pos[i] / local_geo.length()[i])); im[i] = boost::algorithm::clamp(im[i], 0, node_grid[i] - 1); } @@ -74,13 +75,13 @@ LocalBox regular_decomposition(const BoxGeometry &box, Utils::Vector3d local_length; Utils::Vector3d my_left; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { local_length[i] = box.length()[i] / node_grid_par[i]; my_left[i] = node_pos[i] * local_length[i]; } Utils::Array boundaries; - for (std::size_t dir = 0; dir < 3; dir++) { + for (unsigned int dir = 0; dir < 3; dir++) { /* left boundary ? */ boundaries[2 * dir] = (node_pos[dir] == 0); /* right boundary ? */ @@ -106,10 +107,26 @@ void grid_changed_n_nodes() { grid_changed_box_l(box_geo); } +void mpi_set_box_length_local(const Utils::Vector3d &length) { + box_geo.set_length(length); + try { + on_boxl_change(); + } catch (std::exception const &err) { + runtimeErrorMsg() << err.what(); + } +} + +REGISTER_CALLBACK(mpi_set_box_length_local) + void rescale_boxl(int dir, double d_new) { + assert(dir != 3 or ((box_geo.length()[0] == box_geo.length()[1]) and + (box_geo.length()[1] == box_geo.length()[2]))); double scale = (dir - 3) ? d_new * box_geo.length_inv()[dir] : d_new * box_geo.length_inv()[0]; + assert(scale > 0.); + veto_boxl_change(true); + /* If shrinking, rescale the particles first. */ if (scale <= 1.) { mpi_rescale_particles(dir, scale); @@ -118,9 +135,9 @@ void rescale_boxl(int dir, double d_new) { if (dir < 3) { auto box_l = box_geo.length(); box_l[dir] = d_new; - mpi_set_box_length(box_l); + mpi_call_all(mpi_set_box_length_local, box_l); } else { - mpi_set_box_length({d_new, d_new, d_new}); + mpi_call_all(mpi_set_box_length_local, Utils::Vector3d::broadcast(d_new)); } if (scale > 1.) { @@ -128,18 +145,16 @@ void rescale_boxl(int dir, double d_new) { } } -void mpi_set_box_length_local(const Utils::Vector3d &length) { - box_geo.set_length(length); - on_boxl_change(); -} - -REGISTER_CALLBACK(mpi_set_box_length_local) - -void mpi_set_box_length(const Utils::Vector3d &length) { +static void check_new_length(const Utils::Vector3d &length) { if (boost::algorithm::any_of(length, [](double value) { return value <= 0; })) { throw std::domain_error("Box length must be >0"); } + veto_boxl_change(false); +} + +void mpi_set_box_length(const Utils::Vector3d &length) { + check_new_length(length); mpi_call_all(mpi_set_box_length_local, length); } diff --git a/src/core/grid_based_algorithms/lb_particle_coupling.cpp b/src/core/grid_based_algorithms/lb_particle_coupling.cpp index 9fa0e16d57..e6591a92c5 100644 --- a/src/core/grid_based_algorithms/lb_particle_coupling.cpp +++ b/src/core/grid_based_algorithms/lb_particle_coupling.cpp @@ -312,9 +312,11 @@ void lb_lbcoupling_calc_particle_lattice_ia(bool couple_virtual, if (p.is_virtual() and !couple_virtual) return; + auto const folded_pos = folded_position(p.pos(), box_geo); + // Calculate coupling force Utils::Vector3d force = {}; - for (auto pos : positions_in_halo(p.pos(), box_geo)) { + for (auto pos : positions_in_halo(folded_pos, box_geo)) { if (in_local_halo(pos)) { force = lb_viscous_coupling(p, pos, noise_amplitude * f_random(p.id())); @@ -324,7 +326,7 @@ void lb_lbcoupling_calc_particle_lattice_ia(bool couple_virtual, // couple positions including shifts by one box length to add // forces to ghost layers - for (auto pos : positions_in_halo(p.pos(), box_geo)) { + for (auto pos : positions_in_halo(folded_pos, box_geo)) { if (in_local_domain(pos)) { /* if the particle is in our LB volume, this node * is responsible to adding its force */ diff --git a/src/core/immersed_boundary/ImmersedBoundaries.cpp b/src/core/immersed_boundary/ImmersedBoundaries.cpp index c6dfd16391..314cf589cd 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.cpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.cpp @@ -71,7 +71,7 @@ void ImmersedBoundaries::init_volume_conservation(CellStructure &cs) { // reference BoundariesFound = true; if (v->volRef == 0.) { - v->volRef = VolumesCurrent[v->softID]; + v->volRef = VolumesCurrent[static_cast(v->softID)]; } } } @@ -104,7 +104,7 @@ void ImmersedBoundaries::calc_volumes(CellStructure &cs) { // Loop over all particles on local node cs.bond_loop([&tempVol](Particle &p1, int bond_id, Utils::Span partners) { - auto vol_cons_params = vol_cons_parameters(p1); + auto const vol_cons_params = vol_cons_parameters(p1); if (vol_cons_params && boost::get(bonded_ia_params.at(bond_id).get()) != nullptr) { @@ -138,7 +138,7 @@ void ImmersedBoundaries::calc_volumes(CellStructure &cs) { const double v213 = x2[0] * x1[1] * x3[2]; const double v123 = x1[0] * x2[1] * x3[2]; - tempVol[vol_cons_params->softID] += + tempVol[static_cast(vol_cons_params->softID)] += 1.0 / 6.0 * (-v321 + v231 + v312 - v132 - v213 + v123); } return false; @@ -162,11 +162,12 @@ void ImmersedBoundaries::calc_volume_force(CellStructure &cs) { // IBM VolCons bonded interaction. Basically this loops over all // triangles, not all particles. First round to check for volume // conservation. - const IBMVolCons *ibmVolConsParameters = vol_cons_parameters(p1); - if (not ibmVolConsParameters) + auto const vol_cons_params = vol_cons_parameters(p1); + if (not vol_cons_params) return false; - auto current_volume = VolumesCurrent[ibmVolConsParameters->softID]; + auto const current_volume = + VolumesCurrent[static_cast(vol_cons_params->softID)]; // Our particle is the leading particle of a triel // Get second and third particle of the triangle @@ -189,8 +190,8 @@ void ImmersedBoundaries::calc_volume_force(CellStructure &cs) { auto const n = vector_product(a12, a13); const double ln = n.norm(); const double A = 0.5 * ln; - const double fact = ibmVolConsParameters->kappaV * - (current_volume - ibmVolConsParameters->volRef) / + const double fact = vol_cons_params->kappaV * + (current_volume - vol_cons_params->volRef) / current_volume; auto const nHat = n / ln; diff --git a/src/core/immersed_boundary/ImmersedBoundaries.hpp b/src/core/immersed_boundary/ImmersedBoundaries.hpp index af4a3af284..375e5cc5f2 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.hpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.hpp @@ -44,7 +44,7 @@ class ImmersedBoundaries { double get_current_volume(int softID) const { assert(softID >= 0); assert(softID < VolumesCurrent.size()); - return VolumesCurrent[softID]; + return VolumesCurrent[static_cast(softID)]; } private: diff --git a/src/core/integrators/steepest_descent.cpp b/src/core/integrators/steepest_descent.cpp index afeb6315aa..03329516b1 100644 --- a/src/core/integrators/steepest_descent.cpp +++ b/src/core/integrators/steepest_descent.cpp @@ -52,7 +52,7 @@ bool steepest_descent_step(const ParticleRange &particles) { auto f = 0.0; // For all Cartesian coordinates - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { // Skip, if coordinate is fixed if (!p.is_fixed_along(j)) { // Skip positional increments of virtual particles diff --git a/src/core/integrators/velocity_verlet_inline.hpp b/src/core/integrators/velocity_verlet_inline.hpp index 0e819946b5..9538b49eb7 100644 --- a/src/core/integrators/velocity_verlet_inline.hpp +++ b/src/core/integrators/velocity_verlet_inline.hpp @@ -43,7 +43,7 @@ inline void velocity_verlet_propagate_vel_pos(const ParticleRange &particles, // Don't propagate translational degrees of freedom of vs if (p.is_virtual()) continue; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { /* Propagate velocities: v(t+0.5*dt) = v(t) + 0.5 * dt * a(t) */ p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass(); @@ -67,7 +67,7 @@ inline void velocity_verlet_propagate_vel_final(const ParticleRange &particles, if (p.is_virtual()) continue; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { /* Propagate velocity: v(t+dt) = v(t+0.5*dt) + 0.5*dt * a(t+dt) */ p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass(); diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp index c73c41aed2..5d482b8720 100644 --- a/src/core/integrators/velocity_verlet_npt.cpp +++ b/src/core/integrators/velocity_verlet_npt.cpp @@ -31,7 +31,6 @@ #include "grid.hpp" #include "integrate.hpp" #include "npt.hpp" -#include "rotation.hpp" #include "thermostat.hpp" #include "thermostats/npt_inline.hpp" @@ -52,7 +51,7 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, if (p.is_virtual()) continue; auto const noise = friction_therm0_nptiso<2>(npt_iso, p.v(), p.id()); - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { if (nptiso.geometry & nptiso.nptgeom_dir[j]) { nptiso.p_vel[j] += Utils::sqr(p.v()[j] * time_step) * p.mass(); @@ -70,7 +69,7 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, void velocity_verlet_npt_finalize_p_inst(double time_step) { /* finalize derivation of p_inst */ nptiso.p_inst = 0.0; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { if (nptiso.geometry & nptiso.nptgeom_dir[i]) { nptiso.p_vel[i] /= Utils::sqr(time_step); nptiso.p_inst += nptiso.p_vir[i] + nptiso.p_vel[i]; @@ -122,7 +121,7 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, for (auto &p : particles) { if (p.is_virtual()) continue; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { if (nptiso.geometry & nptiso.nptgeom_dir[j]) { p.pos()[j] = scal[1] * (p.pos()[j] + scal[2] * p.v()[j] * time_step); @@ -144,7 +143,7 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, if (this_node == 0) { new_box = box_geo.length(); - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { if (nptiso.cubic_box || nptiso.geometry & nptiso.nptgeom_dir[i]) { new_box[i] = L_new; } @@ -164,13 +163,16 @@ void velocity_verlet_npt_propagate_vel(const ParticleRange &particles, for (auto &p : particles) { #ifdef ROTATION - propagate_omega_quat_particle(p, time_step); + if (p.can_rotate()) { + runtimeErrorMsg() << "The isotropic NpT integrator doesn't propagate " + "angular velocities"; + } #endif // Don't propagate translational degrees of freedom of vs if (p.is_virtual()) continue; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { auto const noise = friction_therm0_nptiso<1>(npt_iso, p.v(), p.id()); if (integ_switch == INTEG_METHOD_NPT_ISO && @@ -196,9 +198,6 @@ void velocity_verlet_npt_step_1(const ParticleRange &particles, void velocity_verlet_npt_step_2(const ParticleRange &particles, double time_step) { velocity_verlet_npt_propagate_vel_final(particles, time_step); -#ifdef ROTATION - convert_torques_propagate_omega(particles, time_step); -#endif velocity_verlet_npt_finalize_p_inst(time_step); } #endif diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index d0a153dbec..026935d43a 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -343,16 +343,18 @@ static void write_le_off(LeesEdwardsBC const &lebc, h5xx::dataset &dataset) { } static void write_le_dir(LeesEdwardsBC const &lebc, h5xx::dataset &dataset) { + auto const shear_direction = static_cast(lebc.shear_direction); auto const extents = static_cast(dataset).extents(); extend_dataset(dataset, Vector2hs{1, 0}); - h5xx::write_dataset(dataset, Utils::Vector{lebc.shear_direction}, + h5xx::write_dataset(dataset, Utils::Vector{shear_direction}, h5xx::slice(Vector2hs{extents[0], 0}, Vector2hs{1, 1})); } static void write_le_normal(LeesEdwardsBC const &lebc, h5xx::dataset &dataset) { + auto const shear_plane_normal = static_cast(lebc.shear_plane_normal); auto const extents = static_cast(dataset).extents(); extend_dataset(dataset, Vector2hs{1, 0}); - h5xx::write_dataset(dataset, Utils::Vector{lebc.shear_plane_normal}, + h5xx::write_dataset(dataset, Utils::Vector{shear_plane_normal}, h5xx::slice(Vector2hs{extents[0], 0}, Vector2hs{1, 1})); } diff --git a/src/core/lees_edwards/LeesEdwardsBC.hpp b/src/core/lees_edwards/LeesEdwardsBC.hpp index e78d205985..916eb31e49 100644 --- a/src/core/lees_edwards/LeesEdwardsBC.hpp +++ b/src/core/lees_edwards/LeesEdwardsBC.hpp @@ -23,12 +23,14 @@ #include #include +#include struct LeesEdwardsBC { + static auto constexpr invalid_dir = 3u; double pos_offset = 0.; double shear_velocity = 0.; - int shear_direction = -1; - int shear_plane_normal = -1; + unsigned int shear_direction = invalid_dir; + unsigned int shear_plane_normal = invalid_dir; Utils::Vector3d distance(Utils::Vector3d const &d, Utils::Vector3d const &l, Utils::Vector3d const &hal_l, Utils::Vector3d const &l_inv, @@ -37,14 +39,16 @@ struct LeesEdwardsBC { Utils::Vector3d n_jumps{}; Utils::Vector3d res = d; - double n_le_crossings = - std::round(res[shear_plane_normal] * l_inv[shear_plane_normal]); - if (n_le_crossings >= 1) - res[shear_direction] += pos_offset; - if (n_le_crossings <= -1) - res[shear_direction] -= pos_offset; + auto const le_plane_normal = static_cast(shear_plane_normal); + auto const le_direction = static_cast(shear_direction); + auto const n_le_crossings = + std::round(res[le_plane_normal] * l_inv[le_plane_normal]); + if (n_le_crossings >= 1.) + res[le_direction] += pos_offset; + if (n_le_crossings <= -1.) + res[le_direction] -= pos_offset; - for (int i : {0, 1, 2}) { + for (auto const i : {0u, 1u, 2u}) { if (periodic[i]) { n_jumps[i] = std::round(res[i] * l_inv[i]); res[i] -= n_jumps[i] * l[i]; diff --git a/src/core/magnetostatics/dp3m.cpp b/src/core/magnetostatics/dp3m.cpp index bad72e967f..ec71f66d50 100644 --- a/src/core/magnetostatics/dp3m.cpp +++ b/src/core/magnetostatics/dp3m.cpp @@ -161,7 +161,7 @@ DipolarP3M::DipolarP3M(P3MParameters &¶meters, double prefactor, } namespace { -template struct AssignDipole { +template struct AssignDipole { void operator()(dp3m_data_struct &dp3m, Utils::Vector3d const &real_pos, Utils::Vector3d const &dip) const { auto const weights = p3m_calculate_interpolation_weights( @@ -188,14 +188,14 @@ void DipolarP3M::dipole_assign(ParticleRange const &particles) { for (auto const &p : particles) { if (p.dipm() != 0.) { - Utils::integral_parameter(dp3m.params.cao, dp3m, - p.pos(), p.calc_dip()); + Utils::integral_parameter(dp3m.params.cao, dp3m, + p.pos(), p.calc_dip()); } } } namespace { -template struct AssignTorques { +template struct AssignTorques { void operator()(dp3m_data_struct const &dp3m, double prefac, int d_rs, ParticleRange const &particles) const { @@ -219,7 +219,7 @@ template struct AssignTorques { } }; -template struct AssignForces { +template struct AssignForces { void operator()(dp3m_data_struct const &dp3m, double prefac, int d_rs, ParticleRange const &particles) const { @@ -394,7 +394,7 @@ double DipolarP3M::kernel(bool force_flag, bool energy_flag, dp3m.sm.spread_grid(dp3m.rs_mesh.data(), comm_cart, dp3m.local_mesh.dim); /* Assign force component from mesh to particle */ - Utils::integral_parameter( + Utils::integral_parameter( dp3m.params.cao, dp3m, dipole_prefac * two_pi_L_i, d_rs, particles); } @@ -481,7 +481,7 @@ double DipolarP3M::kernel(bool force_flag, bool energy_flag, dp3m.sm.spread_grid(Utils::make_span(meshes), comm_cart, dp3m.local_mesh.dim); /* Assign force component from mesh to particle */ - Utils::integral_parameter( + Utils::integral_parameter( dp3m.params.cao, dp3m, dipole_prefac * Utils::sqr(two_pi_L_i), d_rs, particles); } @@ -855,7 +855,7 @@ double dp3m_rtbisection(double box_size, double r_cut_iL, int n_c_part, } void DipolarP3M::sanity_checks_boxl() const { - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { /* check k-space cutoff */ if (dp3m.params.cao_cut[i] >= box_geo.length_half()[i]) { std::stringstream msg; @@ -871,14 +871,14 @@ void DipolarP3M::sanity_checks_boxl() const { } } - if ((box_geo.length()[0] != box_geo.length()[1]) || + if ((box_geo.length()[0] != box_geo.length()[1]) or (box_geo.length()[1] != box_geo.length()[2])) { throw std::runtime_error("DipolarP3M: requires a cubic box"); } } void DipolarP3M::sanity_checks_periodicity() const { - if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { + if (!box_geo.periodic(0) or !box_geo.periodic(1) or !box_geo.periodic(2)) { throw std::runtime_error("DipolarP3M: requires periodicity (1 1 1)"); } } diff --git a/src/core/p3m/common.hpp b/src/core/p3m/common.hpp index ab9cb917a8..20410014f3 100644 --- a/src/core/p3m/common.hpp +++ b/src/core/p3m/common.hpp @@ -49,8 +49,9 @@ auto constexpr P3M_EPSILON_METALLIC = 0.0; #include "LocalBox.hpp" -#include +#include #include +#include namespace detail { /** @brief Index helpers for direct and reciprocal space. @@ -135,9 +136,8 @@ struct P3MParameters { } if (not(mesh >= Utils::Vector3i::broadcast(1) or - ((mesh[0] >= 1) and - (mesh == Utils::Vector3i{{mesh[0], -1, -1}}))) and - not(tuning and mesh == Utils::Vector3i::broadcast(-1))) { + ((mesh[0] >= 1) and (mesh == Utils::Vector3i{{mesh[0], -1, -1}})) or + (tuning and mesh == Utils::Vector3i::broadcast(-1)))) { throw std::domain_error("Parameter 'mesh' must be > 0"); } @@ -150,7 +150,7 @@ struct P3MParameters { } } - if ((cao < 1 or cao > 7) and not(tuning and cao == -1)) { + if ((cao < 1 or cao > 7) and (not tuning or cao != -1)) { throw std::domain_error("Parameter 'cao' must be >= 1 and <= 7"); } @@ -205,7 +205,7 @@ struct P3MLocalMesh { */ void recalc_ld_pos(P3MParameters const ¶ms) { // spatial position of left down mesh point - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { ld_pos[i] = (ld_ind[i] + params.mesh_off[i]) * params.a[i]; } } @@ -243,7 +243,7 @@ std::array, 3> inline calc_meshift( Utils::Vector3i const &mesh_size, bool zero_out_midpoint = false) { std::array, 3> ret{}; - for (std::size_t i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { ret[i] = std::vector(mesh_size[i]); for (int j = 1; j <= mesh_size[i] / 2; j++) { diff --git a/src/core/p3m/interpolation.hpp b/src/core/p3m/interpolation.hpp index ecb6334d08..692d5662af 100644 --- a/src/core/p3m/interpolation.hpp +++ b/src/core/p3m/interpolation.hpp @@ -53,7 +53,7 @@ template struct InterpolationWeights { * type InterpolationWeights. */ class p3m_interpolation_cache { - std::size_t m_cao = 0; + int m_cao = 0; /** Charge fractions for mesh assignment. */ std::vector ca_frac; /** index of first mesh point for charge assignment. */ @@ -109,10 +109,10 @@ class p3m_interpolation_cache { InterpolationWeights ret; ret.ind = ca_fmp[i]; - auto const offset = ca_frac.data() + 3 * i * m_cao; - boost::copy(make_const_span(offset + 0 * m_cao, m_cao), ret.w_x.begin()); - boost::copy(make_const_span(offset + 1 * m_cao, m_cao), ret.w_y.begin()); - boost::copy(make_const_span(offset + 2 * m_cao, m_cao), ret.w_z.begin()); + auto const offset = ca_frac.data() + 3 * i * cao; + boost::copy(make_const_span(offset + 0 * cao, cao), ret.w_x.begin()); + boost::copy(make_const_span(offset + 1 * cao, cao), ret.w_y.begin()); + boost::copy(make_const_span(offset + 2 * cao, cao), ret.w_z.begin()); return ret; } @@ -149,7 +149,7 @@ p3m_calculate_interpolation_weights(const Utils::Vector3d &position, /* nearest mesh point */ Utils::Vector3i nmp; - for (int d = 0; d < 3; d++) { + for (unsigned int d = 0; d < 3; d++) { /* particle position in mesh coordinates */ auto const pos = ((position[d] - local_mesh.ld_pos[d]) * ai[d]) - pos_shift; diff --git a/src/core/particle_node.cpp b/src/core/particle_node.cpp index 9cf4270d21..bd1aebea53 100644 --- a/src/core/particle_node.cpp +++ b/src/core/particle_node.cpp @@ -407,7 +407,7 @@ static int mpi_place_new_particle(int p_id, const Utils::Vector3d &pos) { void mpi_place_particle_local(int pnode, int p_id) { if (pnode == this_node) { - Utils::Vector3d pos; + Utils::Vector3d pos{}; comm_cart.recv(0, some_tag, pos); local_move_particle(p_id, pos); } diff --git a/src/core/pressure_inline.hpp b/src/core/pressure_inline.hpp index a77b0aa3df..5b88813fe5 100644 --- a/src/core/pressure_inline.hpp +++ b/src/core/pressure_inline.hpp @@ -66,10 +66,8 @@ inline void add_non_bonded_pair_virials( auto const force = calc_non_bonded_pair_force(p1, p2, ia_params, d, dist, kernel_forces).f; auto const stress = Utils::tensor_product(d, force); - - auto const type1 = p1.mol_id(); - auto const type2 = p2.mol_id(); - obs_pressure.add_non_bonded_contribution(type1, type2, flatten(stress)); + obs_pressure.add_non_bonded_contribution(p1.type(), p2.type(), p1.mol_id(), + p2.mol_id(), flatten(stress)); } #ifdef ELECTROSTATICS diff --git a/src/core/statistics.cpp b/src/core/statistics.cpp index 0dd94319fe..c11f087f3d 100644 --- a/src/core/statistics.cpp +++ b/src/core/statistics.cpp @@ -133,18 +133,15 @@ Utils::Vector3d angularmomentum(PartCfg &partCfg, int type) { } void momentofinertiamatrix(PartCfg &partCfg, int type, double *MofImatrix) { - int i, count; double massi; Utils::Vector3d p1{}; - count = 0; - for (i = 0; i < 9; i++) + for (unsigned int i = 0; i < 9; i++) MofImatrix[i] = 0.; auto const com = centerofmass(partCfg, type); for (auto const &p : partCfg) { if (type == p.type() and (not p.is_virtual())) { - count++; p1 = p.pos() - com; massi = p.mass(); MofImatrix[0] += massi * (p1[1] * p1[1] + p1[2] * p1[2]); diff --git a/src/core/thermostats/brownian_inline.hpp b/src/core/thermostats/brownian_inline.hpp index 70633dc2a5..9d357f0101 100644 --- a/src/core/thermostats/brownian_inline.hpp +++ b/src/core/thermostats/brownian_inline.hpp @@ -65,7 +65,7 @@ inline Utils::Vector3d bd_drag(Thermostat::GammaType const &brownian_gamma, #endif Utils::Vector3d position = {}; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { // Second (deterministic) term of the Eq. (14.39) of schlick10a. // Only a conservative part of the force is used here #ifdef PARTICLE_ANISOTROPY @@ -118,7 +118,7 @@ inline Utils::Vector3d bd_drag_vel(Thermostat::GammaType const &brownian_gamma, #endif Utils::Vector3d velocity = {}; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { // First (deterministic) term of the eq. (14.34) of schlick10a taking // into account eq. (14.35). Only conservative part of the force is used // here. @@ -171,7 +171,7 @@ inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian, Utils::Vector3d delta_pos_body{}; auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.id()); - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { #ifndef PARTICLE_ANISOTROPY if (sigma_pos > 0.0) { @@ -200,7 +200,7 @@ inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian, #endif Utils::Vector3d position = {}; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { #ifdef PARTICLE_ANISOTROPY position[j] += aniso_flag ? delta_pos_lab[j] : delta_pos_body[j]; @@ -226,7 +226,7 @@ inline Utils::Vector3d bd_random_walk_vel(BrownianThermostat const &brownian, auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.id()); Utils::Vector3d velocity = {}; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { // Random (heat) velocity. See eq. (10.2.16) taking into account eq. // (10.2.18) and (10.2.29), pottier10a. Note, that the pottier10a units @@ -264,7 +264,7 @@ bd_drag_rot(Thermostat::GammaType const &brownian_gamma_rotation, Particle &p, } Utils::Vector3d dphi = {}; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (p.can_rotate_around(j)) { // only a conservative part of the torque is used here #ifndef PARTICLE_ANISOTROPY @@ -303,7 +303,7 @@ bd_drag_vel_rot(Thermostat::GammaType const &brownian_gamma_rotation, } Utils::Vector3d omega = {}; - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (p.can_rotate_around(j)) { #ifdef PARTICLE_ANISOTROPY omega[j] = p.torque()[j] / gamma[j]; @@ -341,7 +341,7 @@ bd_random_walk_rot(BrownianThermostat const &brownian, Particle const &p, Utils::Vector3d dphi = {}; auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.id()); - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (p.can_rotate_around(j)) { #ifndef PARTICLE_ANISOTROPY if (sigma_pos > 0.0) { @@ -376,7 +376,7 @@ bd_random_walk_vel_rot(BrownianThermostat const &brownian, Particle const &p) { Utils::Vector3d domega{}; auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.id()); - for (int j = 0; j < 3; j++) { + for (unsigned int j = 0; j < 3; j++) { if (p.can_rotate_around(j)) { domega[j] = sigma_vel * noise[j] / sqrt(p.rinertia()[j]); } diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 1dab14defe..d1fbe0ccab 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -150,6 +150,9 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, BOOST_REQUIRE_GE(get_particle_node(pid3), 1); } } + set_particle_mol_id(pid1, type_a); + set_particle_mol_id(pid2, type_b); + set_particle_mol_id(pid3, type_b); auto const reset_particle_positions = [&start_positions]() { for (auto const &kv : start_positions) { @@ -214,16 +217,17 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, lennard_jones_set_params(type_b, type_b, eps, sig, cut, shift, offset, min); // matrix indices and reference energy value - auto const max_type = type_b + 1; - auto const n_pairs = Utils::upper_triangular(type_b, type_b, max_type) + 1; - auto const lj_pair_ab = Utils::upper_triangular(type_a, type_b, max_type); - auto const lj_pair_bb = Utils::upper_triangular(type_b, type_b, max_type); + auto const size = std::max(type_a, type_b) + 1; + auto const n_pairs = Utils::upper_triangular(type_b, type_b, size) + 1; + auto const lj_pair_ab = Utils::upper_triangular(type_a, type_b, size); + auto const lj_pair_bb = Utils::upper_triangular(type_b, type_b, size); auto const frac6 = Utils::int_pow<6>(sig / r_off); auto const lj_energy = 4.0 * eps * (Utils::sqr(frac6) - frac6 + shift); // measure energies auto const obs_energy = calculate_energy(); for (int i = 0; i < n_pairs; ++i) { + // particles were set up with type == mol_id auto const ref_inter = (i == lj_pair_ab) ? lj_energy : 0.; auto const ref_intra = (i == lj_pair_bb) ? lj_energy : 0.; BOOST_CHECK_CLOSE(obs_energy->non_bonded_inter[i], ref_inter, 1e-10); @@ -353,5 +357,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, int main(int argc, char **argv) { espresso::system = std::make_unique(argc, argv); - return boost::unit_test::unit_test_main(init_unit_test, argc, argv); + int retval = boost::unit_test::unit_test_main(init_unit_test, argc, argv); + espresso::system.reset(); + return retval; } diff --git a/src/core/unit_tests/MpiCallbacks_test.cpp b/src/core/unit_tests/MpiCallbacks_test.cpp index 3afbe3f2a6..517981d47f 100644 --- a/src/core/unit_tests/MpiCallbacks_test.cpp +++ b/src/core/unit_tests/MpiCallbacks_test.cpp @@ -29,6 +29,7 @@ #include "MpiCallbacks.hpp" #include +#include #include #include @@ -36,6 +37,7 @@ #include #include +static std::weak_ptr mpi_env; static bool called = false; BOOST_AUTO_TEST_CASE(invoke_test) { @@ -111,7 +113,7 @@ BOOST_AUTO_TEST_CASE(callback_model_t) { BOOST_AUTO_TEST_CASE(adding_function_ptr_cb) { boost::mpi::communicator world; - Communication::MpiCallbacks cb(world); + Communication::MpiCallbacks cb(world, ::mpi_env.lock()); void (*fp)(int, const std::string &) = [](int i, const std::string &s) { BOOST_CHECK_EQUAL(537, i); @@ -143,7 +145,7 @@ BOOST_AUTO_TEST_CASE(RegisterCallback) { Communication::RegisterCallback{fp}; boost::mpi::communicator world; - Communication::MpiCallbacks cb(world); + Communication::MpiCallbacks cb(world, ::mpi_env.lock()); called = false; @@ -157,11 +159,12 @@ BOOST_AUTO_TEST_CASE(RegisterCallback) { BOOST_AUTO_TEST_CASE(CallbackHandle) { boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + auto const cbs = + std::make_shared(world, ::mpi_env.lock()); bool m_called = false; Communication::CallbackHandle cb( - &cbs, [&m_called](std::string s) { + cbs, [&m_called](std::string s) { BOOST_CHECK_EQUAL("CallbackHandle", s); m_called = true; @@ -170,7 +173,7 @@ BOOST_AUTO_TEST_CASE(CallbackHandle) { if (0 == world.rank()) { cb(std::string("CallbackHandle")); } else { - cbs.loop(); + cbs->loop(); BOOST_CHECK(called); } } @@ -182,7 +185,7 @@ BOOST_AUTO_TEST_CASE(reduce_callback) { std::plus()); boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + Communication::MpiCallbacks cbs(world, ::mpi_env.lock()); if (0 == world.rank()) { auto const ret = cbs.call(Communication::Result::reduction, @@ -203,7 +206,7 @@ BOOST_AUTO_TEST_CASE(ignore_callback) { Communication::MpiCallbacks::add_static(Communication::Result::ignore, fp); boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + Communication::MpiCallbacks cbs(world, ::mpi_env.lock()); if (0 == world.rank()) { cbs.call(Communication::Result::ignore, fp); @@ -229,7 +232,7 @@ BOOST_AUTO_TEST_CASE(one_rank_callback) { Communication::MpiCallbacks::add_static(Communication::Result::one_rank, fp); boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + Communication::MpiCallbacks cbs(world, ::mpi_env.lock()); if (0 == world.rank()) { BOOST_CHECK_EQUAL(cbs.call(Communication::Result::one_rank, fp), @@ -254,7 +257,7 @@ BOOST_AUTO_TEST_CASE(main_rank_callback) { Communication::MpiCallbacks::add_static(Communication::Result::main_rank, fp); boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + Communication::MpiCallbacks cbs(world, ::mpi_env.lock()); if (0 == world.rank()) { BOOST_CHECK_EQUAL(cbs.call(Communication::Result::main_rank, fp), @@ -273,7 +276,7 @@ BOOST_AUTO_TEST_CASE(call_all) { Communication::MpiCallbacks::add_static(fp); boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + Communication::MpiCallbacks cbs(world, ::mpi_env.lock()); if (0 == world.rank()) { cbs.call_all(fp); @@ -294,7 +297,7 @@ BOOST_AUTO_TEST_CASE(check_exceptions) { Communication::MpiCallbacks::add_static(fp1); boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); + Communication::MpiCallbacks cbs(world, ::mpi_env.lock()); if (0 == world.rank()) { // can't call an unregistered callback @@ -302,11 +305,13 @@ BOOST_AUTO_TEST_CASE(check_exceptions) { } else { // can't call a callback from worker nodes BOOST_CHECK_THROW(cbs.call(fp1), std::logic_error); + cbs.loop(); } } int main(int argc, char **argv) { - boost::mpi::environment mpi_env(argc, argv); + auto const mpi_env = std::make_shared(argc, argv); + ::mpi_env = mpi_env; return boost::unit_test::unit_test_main(init_unit_test, argc, argv); } diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index 4336839652..43947c50cd 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -241,6 +241,7 @@ int main(int argc, char **argv) { if (world.size() == 4) { error_code = boost::unit_test::unit_test_main(init_unit_test, argc, argv); } + espresso::system.reset(); return error_code; } #else // ifdef LENNARD_JONES diff --git a/src/core/unit_tests/energy_test.cpp b/src/core/unit_tests/energy_test.cpp index f04aa0ace3..cd9e9dbf45 100644 --- a/src/core/unit_tests/energy_test.cpp +++ b/src/core/unit_tests/energy_test.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#define BOOST_TEST_MODULE tests +#define BOOST_TEST_MODULE energy calculation #define BOOST_TEST_DYN_LINK #include @@ -69,6 +69,23 @@ BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) { 0.5 * (hadamard_product(p.omega(), p.omega()) * p.rinertia()); BOOST_CHECK_EQUAL(rotational_kinetic_energy(p), expected); } + + // virtual particle + { +#ifdef VIRTUAL_SITES + + Particle p; +#ifdef ROTATIONAL_INERTIA + p.rinertia() = {1., 2., 3.}; +#endif + p.set_virtual(true); + p.omega() = {3., 4., 5.}; + p.set_can_rotate_all_axes(); + + auto const expected = 0.; + BOOST_CHECK_EQUAL(rotational_kinetic_energy(p), expected); +#endif + } #endif } diff --git a/src/core/unit_tests/rotation_test.cpp b/src/core/unit_tests/rotation_test.cpp index 223d3aa01c..450bed3892 100644 --- a/src/core/unit_tests/rotation_test.cpp +++ b/src/core/unit_tests/rotation_test.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -41,13 +42,13 @@ auto constexpr tol = 5. * 100. * std::numeric_limits::epsilon(); namespace Testing { std::tuple, Utils::Vector3d> -setup_trivial_quat(int i, Utils::Vector3d const &v_in) { +setup_trivial_quat(unsigned int i, Utils::Vector3d const &v_in) { auto quat = Utils::Quaternion{{0., 0., 0., 0.}}; quat[i] = 1.; auto v_ref = v_in; - if (i) { + if (i >= 1) { v_ref *= -1.; - v_ref[i - 1] *= -1.; + v_ref[static_cast(i - 1)] *= -1.; } return std::make_tuple(quat, v_ref); } @@ -55,12 +56,12 @@ setup_trivial_quat(int i, Utils::Vector3d const &v_in) { BOOST_AUTO_TEST_CASE(convert_vector_space_to_body_test) { auto const t_in = Utils::Vector3d{{1., 2., 3.}}; - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { auto p = Particle(); Utils::Vector3d t_ref; std::tie(p.quat(), t_ref) = Testing::setup_trivial_quat(i, t_in); auto const t_out = convert_vector_space_to_body(p, t_in); - for (int j : {0, 1, 2}) { + for (unsigned int j : {0u, 1u, 2u}) { BOOST_CHECK_CLOSE(t_out[j], t_ref[j], tol); } } @@ -70,7 +71,7 @@ BOOST_AUTO_TEST_CASE(convert_torque_to_body_frame_apply_fix_test) { auto const t_in = Utils::Vector3d{{1., 2., 3.}}; { // test particle torque conversion - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { auto p = Particle(); p.set_can_rotate_all_axes(); Utils::Vector3d t_ref; @@ -78,14 +79,14 @@ BOOST_AUTO_TEST_CASE(convert_torque_to_body_frame_apply_fix_test) { p.torque() = t_in; convert_torque_to_body_frame_apply_fix(p); auto const t_out = p.torque(); - for (int j : {0, 1, 2}) { + for (unsigned int j : {0u, 1u, 2u}) { BOOST_CHECK_CLOSE(t_out[j], t_ref[j], tol); } } } { // torque is set to zero for axes without rotation - for (int j : {0, 1, 2}) { + for (unsigned int j : {0u, 1u, 2u}) { auto p = Particle(); p.set_can_rotate_all_axes(); p.set_can_rotate_around(j, false); @@ -139,7 +140,7 @@ BOOST_AUTO_TEST_CASE(rotate_particle_body_test) { auto const phi = Utils::pi(); auto const quat = local_rotate_particle_body(p, {0., 0., 1.}, phi); auto const quat_ref = Utils::Vector4d{{-4., 3., -2., 1.}}; - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); } } @@ -149,7 +150,7 @@ BOOST_AUTO_TEST_CASE(rotate_particle_body_test) { auto const phi = 2. * Utils::pi(); auto const quat = local_rotate_particle_body(p, {0., 0., 1.}, phi); auto const quat_ref = Utils::Vector4d{{-1., -2., -3., -4.}}; - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); } } @@ -165,7 +166,7 @@ BOOST_AUTO_TEST_CASE(propagate_omega_quat_particle_test) { propagate_omega_quat_particle(p, 0.01); auto const quat = p.quat(); auto const quat_ref = Utils::Quaternion::identity(); - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); } } @@ -173,7 +174,7 @@ BOOST_AUTO_TEST_CASE(propagate_omega_quat_particle_test) { // test trivial cases with parameters extremely close to the limit: // time step almost 1.0 and product of time step with omega almost 2.0 auto const time_step = 0.99; - for (int j : {0, 1, 2}) { + for (unsigned int j : {0u, 1u, 2u}) { p.quat() = Utils::Quaternion::identity(); p.omega() = {0., 0., 0.}; p.omega()[j] = 2.; @@ -182,7 +183,7 @@ BOOST_AUTO_TEST_CASE(propagate_omega_quat_particle_test) { auto quat_ref = Utils::Quaternion::identity(); quat_ref[1 + j] = time_step; quat_ref[0] = std::sqrt(1. - time_step * time_step); - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); } } @@ -207,8 +208,8 @@ BOOST_AUTO_TEST_CASE(convert_operator_body_to_space_test) { auto const linear_transf_space = convert_body_to_space(quat, linear_transf_body); - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (unsigned int i = 0; i < 3; i++) { + for (unsigned int j = 0; j < 3; j++) { if (linear_transf_space_ref(i, j) == 0.0) { BOOST_CHECK_SMALL( std::abs(linear_transf_space(i, j) - linear_transf_space_ref(i, j)), @@ -233,7 +234,7 @@ BOOST_AUTO_TEST_CASE(convert_dip_to_quat_test) { auto const pair = convert_dip_to_quat({0., 0., dipm}); auto const quat = quat_to_vector4d(pair.first); auto const quat_ref = Utils::Vector4d{{1., 0., 0., 0.}}; - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); } BOOST_CHECK_CLOSE(pair.second, dipm, tol); @@ -243,7 +244,7 @@ BOOST_AUTO_TEST_CASE(convert_dip_to_quat_test) { auto const pair = convert_dip_to_quat({dipm, 0., 0.}); auto const quat = quat_to_vector4d(pair.first); auto const quat_ref = Utils::Vector4d{{0.5, -0.5, 0.5, -0.5}}; - for (int i : {0, 1, 2, 3}) { + for (unsigned int i : {0u, 1u, 2u, 3u}) { BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); } BOOST_CHECK_CLOSE(pair.second, dipm, tol); diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp index 3e40e15f39..6d97a055d7 100644 --- a/src/core/virtual_sites.cpp +++ b/src/core/virtual_sites.cpp @@ -120,9 +120,9 @@ calculate_vs_relate_to_params(Particle const &p_current, // Verify result Utils::Quaternion qtemp = relate_to_quat * quat; - for (int i = 0; i < 4; i++) + for (unsigned int i = 0; i < 4; i++) if (fabs(qtemp[i] - quat_director[i]) > 1E-9) - fprintf(stderr, "vs_relate_to: component %d: %f instead of %f\n", i, + fprintf(stderr, "vs_relate_to: component %u: %f instead of %f\n", i, qtemp[i], quat_director[i]); } return std::make_tuple(quat, dist); diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp index 4151efc3f0..1bd24d03bf 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp @@ -30,6 +30,14 @@ #include +static void check_no_vs_exist(char const *const message) { + if (std::any_of(cell_structure.local_particles().begin(), + cell_structure.local_particles().end(), + [](Particle const &p) { return p.is_virtual(); })) { + runtimeErrorMsg() << "Inertialess Tracers: " << message; + } +} + void VirtualSitesInertialessTracers::after_force_calc() { // Now the forces are computed and need to go into the LB fluid if (lattice_switch == ActiveLB::CPU) { @@ -39,16 +47,15 @@ void VirtualSitesInertialessTracers::after_force_calc() { #ifdef CUDA if (lattice_switch == ActiveLB::GPU) { IBM_ForcesIntoFluid_GPU(cell_structure.local_particles(), this_node); + if (comm_cart.size() != 1 and this_node != 0) { + check_no_vs_exist("The LB GPU method cannot integrate virtual sites when " + "more than 1 MPI ranks are used. The particles on MPI " + "rank >= 2 are now in an undeterminate state."); + } return; } #endif - if (std::any_of(cell_structure.local_particles().begin(), - cell_structure.local_particles().end(), - [](Particle &p) { return p.is_virtual(); })) { - runtimeErrorMsg() << "Inertialess Tracers: No LB method was active but " - "virtual sites present."; - return; - } + check_no_vs_exist("No LB method was active but virtual sites present."); } void VirtualSitesInertialessTracers::after_lb_propagation(double time_step) { diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index 87f29d1a6d..3c15a01dce 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -92,7 +92,7 @@ void IBM_UpdateParticlePositions(ParticleRange const &particles, // Euler integrator for (auto &p : particles) { if (p.is_virtual()) { - for (int axis = 0; axis < 2; axis++) { + for (unsigned int axis = 0; axis < 3; axis++) { #ifdef EXTERNAL_FORCES if (not p.is_fixed_along(axis)) #endif diff --git a/src/python/espressomd/CMakeLists.txt b/src/python/espressomd/CMakeLists.txt index 188e3e7db1..a508e799e3 100644 --- a/src/python/espressomd/CMakeLists.txt +++ b/src/python/espressomd/CMakeLists.txt @@ -66,13 +66,17 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") Espresso_pyx_flags INTERFACE -Wno-pedantic -Wno-cpp -Wno-strict-aliasing -Wno-maybe-uninitialized -Wno-unused-variable - -Wno-deprecated-declarations) + -Wno-deprecated-declarations -Wno-volatile) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") target_compile_options( Espresso_pyx_flags INTERFACE -Wno-pedantic -Wno-\#warnings -Wno-sometimes-uninitialized -Wno-unused-variable -Wno-deprecated-declarations) + if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + target_compile_options(Espresso_pyx_flags + INTERFACE -Wno-missing-field-initializers) + endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") target_compile_options(Espresso_pyx_flags INTERFACE -wd1224) else() @@ -105,10 +109,9 @@ foreach(cython_file ${cython_SRC}) add_custom_command( OUTPUT ${outputpath} COMMAND - ${CYTHON_EXECUTABLE} $<$:--warning-errors> - -3 --cplus --directive embedsignature=True --directive binding=True -I - ${CMAKE_CURRENT_SOURCE_DIR} -I ${CMAKE_CURRENT_BINARY_DIR} - ${cython_file} -o ${outputpath} + ${CYTHON_EXECUTABLE} -3 --cplus --directive embedsignature=True + --directive binding=True -I ${CMAKE_CURRENT_SOURCE_DIR} -I + ${CMAKE_CURRENT_BINARY_DIR} ${cython_file} -o ${outputpath} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.. DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/myconfig.pxi ${cython_file} ${cython_HEADER}) diff --git a/src/python/espressomd/_init.pyx b/src/python/espressomd/_init.pyx index 0ef6ee52f9..0b96999140 100644 --- a/src/python/espressomd/_init.pyx +++ b/src/python/espressomd/_init.pyx @@ -17,6 +17,7 @@ # along with this program. If not, see . # import sys +import atexit from . cimport script_interface from . cimport communication from libcpp.memory cimport shared_ptr @@ -28,7 +29,17 @@ communication.init(mpi_env) # Initialize script interface # Has to be _after_ mpi_init -script_interface.init(communication.mpiCallbacks()) +script_interface.init(communication.mpiCallbacksHandle()) + + +def session_shutdown(): + mpi_env.reset() + communication.deinit() + script_interface.deinit() + + +atexit.register(session_shutdown) + # Block the worker nodes in the callback loop. # The head node is just returning to the user script. diff --git a/src/python/espressomd/communication.pxd b/src/python/espressomd/communication.pxd index aeb4f43adf..7065f96a9d 100644 --- a/src/python/espressomd/communication.pxd +++ b/src/python/espressomd/communication.pxd @@ -30,4 +30,6 @@ cdef extern from "communication.hpp": cdef extern from "communication.hpp" namespace "Communication": MpiCallbacks & mpiCallbacks() + shared_ptr[MpiCallbacks] mpiCallbacksHandle() void init(shared_ptr[environment]) + void deinit() diff --git a/src/python/espressomd/constraints.py b/src/python/espressomd/constraints.py index eb0dba268c..79217173de 100644 --- a/src/python/espressomd/constraints.py +++ b/src/python/espressomd/constraints.py @@ -183,8 +183,19 @@ def total_normal_force(self): @script_interface_register class HomogeneousMagneticField(Constraint): - """ + Homogeneous magnetic field :math:`\\vec{H}`. + The resulting force :math:`\\vec{F}`, torque :math:`\\vec{\\tau}` + and energy `U` on the particles are then + + :math:`\\vec{F} = \\vec{0}` + + :math:`\\vec{\\tau} = \\vec{\\mu} \\times \\vec{H}` + + :math:`U = -\\vec{\\mu} \\cdot \\vec{H}` + + where :math:`\\vec{\\mu}` is the particle dipole moment. + Attributes ---------- H : (3,) array_like of :obj:`float` @@ -393,12 +404,12 @@ class Gravity(Constraint): """ Gravity force - :math:`F = m \\cdot g` + :math:`\\vec{F} = m \\cdot \\vec{g}` Arguments ---------- g : (3,) array_like of :obj:`float` - The gravitational acceleration. + The gravitational constant. """ @@ -420,21 +431,21 @@ class LinearElectricPotential(Constraint): """ Electric potential of the form - :math:`\\phi = -E \\cdot x + \\phi_0`, + :math:`\\phi = -\\vec{E} \\cdot \\vec{x} + \\phi_0`, - resulting in the electric field E - everywhere. (E.g. in a plate capacitor). + resulting in the electric field :math:`\\vec{E}` everywhere. The resulting force on the particles are then - :math:`F = q \\cdot E` + :math:`\\vec{F} = q \\cdot \\vec{E}` - where :math:`q` is the charge of the particle. + where :math:`q` and :math:`\\vec{x}` are the particle charge and position + in folded coordinates. + This can be used to model a plate capacitor. Arguments ---------- E : array_like of :obj:`float` The electric field. - phi0 : :obj:`float` The potential at the origin @@ -463,15 +474,18 @@ class ElectricPlaneWave(Constraint): """ Electric field of the form - :math:`E = E0 \\cdot \\sin(k \\cdot x + \\omega \\cdot t + \\phi)` + :math:`\\vec{E} = \\vec{E_0} \\cdot \\sin(\\vec{k} \\cdot \\vec{x} + \\omega \\cdot t + \\phi)` The resulting force on the particles are then - :math:`F = q \\cdot E` + :math:`\\vec{F} = q \\cdot \\vec{E}` - where :math:`q` is the charge of the particle. + where :math:`q` and :math:`\\vec{x}` are the particle charge and position + in folded coordinates. This can be used to generate a homogeneous AC - field by setting k to zero. + field by setting :math:`\\vec{k}` to the null vector. + For periodic systems, :math:`\\vec{k}` must be an integer multiple + of :math:`2\\pi \\vec{L}^{-1}` with :math:`\\vec{L}` the box length. Arguments ---------- @@ -482,7 +496,7 @@ class ElectricPlaneWave(Constraint): omega : :obj:`float` Frequency of the wave phi : :obj:`float`, optional - Phase shift + Phase """ @@ -520,9 +534,10 @@ class FlowField(_Interpolated): Viscous coupling to a flow field that is interpolated from tabulated data like - :math:`F = -\\gamma \\cdot \\left( u(r) - v \\right)` + :math:`\\vec{F} = -\\gamma \\cdot \\left( \\vec{u}(\\vec{x}) - \\vec{v} \\right)` - where :math:`v` is the velocity of the particle. + where :math:`\\vec{v}` and :math:`\\vec{x}` are the particle velocity and position + in folded coordinates, and :math:`\\vec{u}(\\vec{x})` is a 3D flow field on a grid. Arguments ---------- @@ -549,9 +564,10 @@ class HomogeneousFlowField(Constraint): Viscous coupling to a flow field that is constant in space with the force - :math:`F = -\\gamma \\cdot (u - v)` + :math:`\\vec{F} = -\\gamma \\cdot (\\vec{u} - \\vec{v})` - where :math:`v` is the velocity of the particle. + where :math:`\\vec{v}` is the velocity of the particle + and :math:`\\vec{u}` is the constant flow field. Attributes ---------- @@ -580,11 +596,11 @@ class ElectricPotential(_Interpolated): """ Electric potential interpolated from - provided data. The electric field E is + provided data. The electric field :math:`\\vec{E}` is calculated numerically from the potential, and the resulting force on the particles are - :math:`F = q \\cdot E` + :math:`\\vec{F} = q \\cdot \\vec{E}` where :math:`q` is the charge of the particle. diff --git a/src/python/espressomd/cuda_init.pxd b/src/python/espressomd/cuda_init.pxd index 1336553123..8cbd781488 100644 --- a/src/python/espressomd/cuda_init.pxd +++ b/src/python/espressomd/cuda_init.pxd @@ -22,7 +22,7 @@ from libcpp.vector cimport vector cdef extern from "cuda_init.hpp": cdef struct EspressoGpuDevice: int id - char name[64] + char name[256] char proc_name[64] int node int compute_capability_major @@ -33,5 +33,5 @@ cdef extern from "cuda_init.hpp": void cuda_set_device(int dev) except + int cuda_get_device() except + int cuda_get_n_gpus() except + - void cuda_get_gpu_name(int dev, char name[64]) except + + void cuda_get_gpu_name(int dev, char * name) except + vector[EspressoGpuDevice] cuda_gather_gpus() diff --git a/src/python/espressomd/cuda_init.pyx b/src/python/espressomd/cuda_init.pyx index 1a34f02579..71c794c756 100644 --- a/src/python/espressomd/cuda_init.pyx +++ b/src/python/espressomd/cuda_init.pyx @@ -63,7 +63,7 @@ cdef class CudaInitHandle: List of available CUDA devices. """ - cdef char gpu_name_buffer[4 + 64] + cdef char gpu_name_buffer[256] n_gpus = 0 try: n_gpus = cuda_get_n_gpus() diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index e567b587a2..21ebd97f45 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -2888,8 +2888,6 @@ class BondedInteractions(ScriptObjectMap): return bond_id def __getitem__(self, bond_id): - self._assert_key_type(bond_id) - if self.call_method('has_bond', bond_id=bond_id): bond_obj = self.call_method('get_bond', bond_id=bond_id) bond_obj._bond_id = bond_id @@ -2932,7 +2930,6 @@ class BondedInteractions(ScriptObjectMap): bond_id = self.call_method("insert", object=bond_obj) else: # Throw error if attempting to overwrite a bond of different type - self._assert_key_type(bond_id) if self.call_method("contains", key=bond_id): old_type = bonded_interaction_classes[ get_bonded_interaction_type_from_es_core(bond_id)] @@ -2969,3 +2966,14 @@ class BondedInteractions(ScriptObjectMap): for bond_id, (bond_params, bond_type) in params.items(): self[bond_id] = bonded_interaction_classes[bond_type]( **bond_params) + + def __reduce__(self): + so_callback, (so_name, so_bytestring) = super().__reduce__() + return (BondedInteractions._restore_object, + (so_callback, (so_name, so_bytestring), self.__getstate__())) + + @classmethod + def _restore_object(cls, so_callback, so_callback_args, state): + so = so_callback(*so_callback_args) + so.__setstate__(state) + return so diff --git a/src/python/espressomd/observables.py b/src/python/espressomd/observables.py index 2de2c1910f..cf22b3c43b 100644 --- a/src/python/espressomd/observables.py +++ b/src/python/espressomd/observables.py @@ -28,7 +28,7 @@ class Observable(ScriptInterfaceHelper): Methods ------- shape() - Return the shape of the observable. + Get the shape of the numpy array returned by the observable. """ _so_name = "Observables::Observable" _so_bind_methods = ("shape",) @@ -96,9 +96,14 @@ class ComPosition(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ComPosition" @@ -118,9 +123,14 @@ class ComVelocity(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ComVelocity" @@ -154,9 +164,14 @@ class DensityProfile(ProfileObservable): max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (``n_x_bins``, ``n_y_bins``, ``n_z_bins``) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::DensityProfile" @@ -165,7 +180,7 @@ class DensityProfile(ProfileObservable): @script_interface_register class DipoleMoment(Observable): - """Calculates the dipole moment for particles with given ids. + """Calculates the electric dipole moment for particles with given ids. Output format: :math:`\\left(\\sum_i q_i r^x_i, \\sum_i q_i r^y_i, \\sum_i q_i r^z_i\\right)` @@ -174,9 +189,14 @@ class DipoleMoment(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::DipoleMoment" @@ -210,11 +230,16 @@ class FluxDensityProfile(ProfileObservable): max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_x_bins``, ``n_y_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the x, y and z - components of the flux density. + calculate() + Run the observable. + + Returns + ------- + (``n_x_bins``, ``n_y_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the x, + y and z components of the flux density, respectively. """ _so_name = "Observables::FluxDensityProfile" @@ -248,11 +273,16 @@ class ForceDensityProfile(ProfileObservable): max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_x_bins``, ``n_y_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the x, y and z - components of the force. + calculate() + Run the observable. + + Returns + ------- + (``n_x_bins``, ``n_y_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the x, + y and z components of the force, respectively. """ _so_name = "Observables::ForceDensityProfile" @@ -302,11 +332,16 @@ class LBVelocityProfile(ProfileObservable): allow_empty_bins : :obj:`bool`, default=False Whether or not to allow bins that will not be sampled at all. - Returns + Methods ------- - (``n_x_bins``, ``n_y_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the x, y and z - components of the LB velocity. + calculate() + Run the observable. + + Returns + ------- + (``n_x_bins``, ``n_y_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the x, + y and z components of the LB velocity, respectively. """ _so_name = "Observables::LBVelocityProfile" @@ -321,9 +356,14 @@ class LBFluidPressureTensor(Observable): ---------- None - Returns + Methods ------- - (3, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::LBFluidPressureTensor" @@ -341,9 +381,14 @@ class MagneticDipoleMoment(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::MagneticDipoleMoment" @@ -363,9 +408,14 @@ class ParticleAngularVelocities(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticleAngularVelocities" @@ -388,9 +438,14 @@ class ParticleBodyAngularVelocities(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticleBodyAngularVelocities" @@ -413,9 +468,14 @@ class ParticleBodyVelocities(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticleBodyVelocities" @@ -435,9 +495,14 @@ class ParticleForces(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticleForces" @@ -457,9 +522,14 @@ class ParticlePositions(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticlePositions" @@ -479,9 +549,14 @@ class ParticleVelocities(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticleVelocities" @@ -498,9 +573,14 @@ class ParticleDistances(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N - 1,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N - 1,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::ParticleDistances" @@ -520,9 +600,14 @@ class TotalForce(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::TotalForce" @@ -539,9 +624,14 @@ class BondAngles(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N - 2,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N - 2,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::BondAngles" @@ -560,9 +650,14 @@ class CosPersistenceAngles(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N - 2,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N - 2,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::CosPersistenceAngles" @@ -579,9 +674,14 @@ class BondDihedrals(Observable): ids : array_like of :obj:`int` The ids of (existing) particles to take into account. - Returns + Methods ------- - (N - 3,) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (N - 3,) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::BondDihedrals" @@ -592,9 +692,14 @@ class Energy(Observable): """Calculates the total energy. - Returns + Methods ------- - :obj:`float` + calculate() + Run the observable. + + Returns + ------- + :obj:`float` """ _so_name = "Observables::Energy" @@ -605,9 +710,14 @@ class Pressure(Observable): """Calculates the total scalar pressure. - Returns + Methods ------- - :obj:`float` + calculate() + Run the observable. + + Returns + ------- + :obj:`float` """ _so_name = "Observables::Pressure" @@ -618,9 +728,14 @@ class PressureTensor(Observable): """Calculates the total pressure tensor. - Returns + Methods ------- - (3, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::PressureTensor" @@ -636,9 +751,14 @@ class DPDStress(Observable): ---------- None - Returns + Methods ------- - (3, 3) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (3, 3) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::DPDStress" @@ -663,20 +783,25 @@ class CylindricalDensityProfile(CylindricalProfileObservable): Number of bins in ``z`` direction. min_r : :obj:`float`, default = 0 Minimum ``r`` to consider. - min_phi : :obj:`float`, default = -pi - Minimum ``phi`` to consider. Must be in [-pi,pi). + min_phi : :obj:`float`, default = :math:`-\\pi` + Minimum ``phi`` to consider. Must be in :math:`[-\\pi,\\pi)`. min_z : :obj:`float` Minimum ``z`` to consider. max_r : :obj:`float` Maximum ``r`` to consider. - max_phi : :obj:`float`, default = pi - Maximum ``phi`` to consider. Must be in (-pi,pi]. + max_phi : :obj:`float`, default = :math:`\\pi` + Maximum ``phi`` to consider. Must be in :math:`(-\\pi,\\pi]`. max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``) :obj:`ndarray` of :obj:`float` + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``) :obj:`ndarray` of :obj:`float` """ _so_name = "Observables::CylindricalDensityProfile" @@ -701,22 +826,28 @@ class CylindricalFluxDensityProfile(CylindricalProfileObservable): Number of bins in ``z`` direction. min_r : :obj:`float`, default = 0 Minimum ``r`` to consider. - min_phi : :obj:`float`, default = -pi - Minimum ``phi`` to consider. Must be in [-pi,pi). + min_phi : :obj:`float`, default = :math:`-\\pi` + Minimum ``phi`` to consider. Must be in :math:`[-\\pi,\\pi)`. min_z : :obj:`float` Minimum ``z`` to consider. max_r : :obj:`float` Maximum ``r`` to consider. - max_phi : :obj:`float`, default = pi - Maximum ``phi`` to consider. Must be in (-pi,pi]. + max_phi : :obj:`float`, default = :math:`\\pi` + Maximum ``phi`` to consider. Must be in :math:`(-\\pi,\\pi]`. max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the radial distance, - azimuth and axial coordinate of the particle flux density field. + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the + radial distance, azimuth and axial coordinate of the particle + flux density field, respectively. """ _so_name = "Observables::CylindricalFluxDensityProfile" @@ -743,22 +874,28 @@ class CylindricalLBFluxDensityProfileAtParticlePositions( Number of bins in ``z`` direction. min_r : :obj:`float`, default = 0 Minimum ``r`` to consider. - min_phi : :obj:`float`, default = -pi - Minimum ``phi`` to consider. Must be in [-pi,pi). + min_phi : :obj:`float`, default = :math:`-\\pi` + Minimum ``phi`` to consider. Must be in :math:`[-\\pi,\\pi)`. min_z : :obj:`float` Minimum ``z`` to consider. max_r : :obj:`float` Maximum ``r`` to consider. - max_phi : :obj:`float`, default = pi - Maximum ``phi`` to consider. Must be in (-pi,pi]. + max_phi : :obj:`float`, default = :math:`\\pi` + Maximum ``phi`` to consider. Must be in :math:`(-\\pi,\\pi]`. max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the radial distance, - azimuth and axial coordinate of the LB flux density field. + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the + radial distance, azimuth and axial coordinate of the LB flux + density field, respectively. """ _so_name = "Observables::CylindricalLBFluxDensityProfileAtParticlePositions" @@ -785,22 +922,28 @@ class CylindricalLBVelocityProfileAtParticlePositions( Number of bins in ``z`` direction. min_r : :obj:`float`, default = 0 Minimum ``r`` to consider. - min_phi : :obj:`float`, default = -pi - Minimum ``phi`` to consider. Must be in [-pi,pi). + min_phi : :obj:`float`, default = :math:`-\\pi` + Minimum ``phi`` to consider. Must be in :math:`[-\\pi,\\pi)`. min_z : :obj:`float` Minimum ``z`` to consider. max_r : :obj:`float` Maximum ``r`` to consider. - max_phi : :obj:`float`, default = pi - Maximum ``phi`` to consider. Must be in (-pi,pi]. + max_phi : :obj:`float`, default = :math:`\\pi` + Maximum ``phi`` to consider. Must be in :math:`(-\\pi,\\pi]`. max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the radial distance, - azimuth and axial coordinate of the LB velocity field. + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the + radial distance, azimuth and axial coordinate of the LB velocity + field, respectively. """ _so_name = "Observables::CylindricalLBVelocityProfileAtParticlePositions" @@ -825,22 +968,28 @@ class CylindricalVelocityProfile(CylindricalProfileObservable): Number of bins in ``z`` direction. min_r : :obj:`float`, default = 0 Minimum ``r`` to consider. - min_phi : :obj:`float`, default = -pi - Minimum ``phi`` to consider. Must be in [-pi,pi). + min_phi : :obj:`float`, default = :math:`-\\pi` + Minimum ``phi`` to consider. Must be in :math:`[-\\pi,\\pi)`. min_z : :obj:`float` Minimum ``z`` to consider. max_r : :obj:`float` Maximum ``r`` to consider. - max_phi : :obj:`float`, default = pi - Maximum ``phi`` to consider. Must be in (-pi,pi]. + max_phi : :obj:`float`, default = :math:`\\pi` + Maximum ``phi`` to consider. Must be in :math:`(-\\pi,\\pi]`. max_z : :obj:`float` Maximum ``z`` to consider. - Returns + Methods ------- - (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the radial distance, - azimuth and axial coordinate of the particle velocity field. + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the + radial distance, azimuth and axial coordinate of the particle + velocity field, respectively. """ _so_name = "Observables::CylindricalVelocityProfile" @@ -867,24 +1016,30 @@ class CylindricalLBVelocityProfile(CylindricalProfileObservable): Number of bins in ``z`` direction. min_r : :obj:`float`, default = 0 Minimum ``r`` to consider. - min_phi : :obj:`float`, default = -pi - Minimum ``phi`` to consider. Must be in [-pi,pi). + min_phi : :obj:`float`, default = :math:`-\\pi` + Minimum ``phi`` to consider. Must be in :math:`[-\\pi,\\pi)`. min_z : :obj:`float` Minimum ``z`` to consider. max_r : :obj:`float` Maximum ``r`` to consider. - max_phi : :obj:`float`, default = pi - Maximum ``phi`` to consider. Must be in (-pi,pi]. + max_phi : :obj:`float`, default = :math:`\\pi` + Maximum ``phi`` to consider. Must be in :math:`(-\\pi,\\pi]`. max_z : :obj:`float` Maximum ``z`` to consider. sampling_density : :obj:`float` Samples per unit volume for the LB velocity interpolation. - Returns + Methods ------- - (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` - The fourth component contains the histogram for the radial distance, - azimuth and axial coordinate of the LB velocity field. + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``, ``n_phi_bins``, ``n_z_bins``, 3) :obj:`ndarray` of :obj:`float` + The fourth dimension of the array stores the histogram for the + radial distance, azimuth and axial coordinate of the LB velocity + field, respectively. """ _so_name = "Observables::CylindricalLBVelocityProfile" @@ -910,10 +1065,15 @@ class RDF(Observable): max_r : :obj:`float` Maximum ``r`` to consider. - Returns + Methods ------- - (``n_r_bins``,) :obj:`ndarray` of :obj:`float` - The RDF. + calculate() + Run the observable. + + Returns + ------- + (``n_r_bins``,) :obj:`ndarray` of :obj:`float` + The RDF. """ _so_name = "Observables::RDF" diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index 862ce23c80..e4bad81666 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -541,7 +541,7 @@ cdef class ParticleHandle: rinertia : (3,) array_like of :obj:`float` - Sets the diagonal elements of this particles rotational inertia + Sets the diagonal elements of this particle's rotational inertia tensor. These correspond with the inertial moments along the coordinate axes in the particle's co-rotating coordinate system. When the particle's quaternions are set to ``[1, 0, 0, 0,]``, the diff --git a/src/python/espressomd/script_interface.pxd b/src/python/espressomd/script_interface.pxd index bd2b0c2860..5e9bd38ddf 100644 --- a/src/python/espressomd/script_interface.pxd +++ b/src/python/espressomd/script_interface.pxd @@ -58,7 +58,7 @@ cdef extern from "script_interface/ContextManager.hpp" namespace "ScriptInterfac cdef extern from "script_interface/ContextManager.hpp" namespace "ScriptInterface": cdef cppclass ContextManager: - ContextManager(MpiCallbacks & , const Factory[ObjectHandle] & ) + ContextManager(const shared_ptr[MpiCallbacks] & , const Factory[ObjectHandle] & ) shared_ptr[ObjectHandle] make_shared(CreationPolicy, const string &, const VariantMap) except + shared_ptr[ObjectHandle] deserialize(const string &) except + string serialize(const ObjectHandle *) except + @@ -69,4 +69,5 @@ cdef extern from "script_interface/initialize.hpp" namespace "ScriptInterface": cdef extern from "script_interface/get_value.hpp" namespace "ScriptInterface": T get_value[T](const Variant T) -cdef void init(MpiCallbacks &) +cdef void init(const shared_ptr[MpiCallbacks] &) +cdef void deinit() diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index b2deda5e94..aed9f781d3 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -406,15 +406,6 @@ class ScriptObjectList(ScriptInterfaceHelper): """ - def __init__(self, *args, **kwargs): - if args: - params, (_unpickle_so_class, (_so_name, bytestring)) = args - assert _so_name == self._so_name - self = _unpickle_so_class(_so_name, bytestring) - self.__setstate__(params) - else: - super().__init__(**kwargs) - def __getitem__(self, key): return self.call_method("get_elements")[key] @@ -426,24 +417,6 @@ class ScriptObjectList(ScriptInterfaceHelper): def __len__(self): return self.call_method("size") - @classmethod - def _restore_object(cls, so_callback, so_callback_args, state): - so = so_callback(*so_callback_args) - so.__setstate__(state) - return so - - def __reduce__(self): - so_callback, (so_name, so_bytestring) = super().__reduce__() - return (ScriptObjectList._restore_object, - (so_callback, (so_name, so_bytestring), self.__getstate__())) - - def __getstate__(self): - return self.call_method("get_elements") - - def __setstate__(self, object_list): - for item in object_list: - self.add(item) - class ScriptObjectMap(ScriptInterfaceHelper): """ @@ -456,17 +429,6 @@ class ScriptObjectMap(ScriptInterfaceHelper): """ - _key_type = int - - def __init__(self, *args, **kwargs): - if args: - params, (_unpickle_so_class, (_so_name, bytestring)) = args - assert _so_name == self._so_name - self = _unpickle_so_class(_so_name, bytestring) - self.__setstate__(params) - else: - super().__init__(**kwargs) - def remove(self, key): """ Remove the element with the given key. @@ -485,15 +447,12 @@ class ScriptObjectMap(ScriptInterfaceHelper): return self.call_method("size") def __getitem__(self, key): - self._assert_key_type(key) return self.call_method("get", key=key) def __setitem__(self, key, value): - self._assert_key_type(key) self.call_method("insert", key=key, object=value) def __delitem__(self, key): - self._assert_key_type(key) self.call_method("erase", key=key) def keys(self): @@ -505,28 +464,6 @@ class ScriptObjectMap(ScriptInterfaceHelper): def items(self): for k in self.keys(): yield k, self[k] - def _assert_key_type(self, key): - if not utils.is_valid_type(key, self._key_type): - raise TypeError(f"Key has to be of type {self._key_type.__name__}") - - @classmethod - def _restore_object(cls, so_callback, so_callback_args, state): - so = so_callback(*so_callback_args) - so.__setstate__(state) - return so - - def __reduce__(self): - so_callback, (so_name, so_bytestring) = super().__reduce__() - return (ScriptObjectMap._restore_object, - (so_callback, (so_name, so_bytestring), self.__getstate__())) - - def __getstate__(self): - return dict(self.items()) - - def __setstate__(self, params): - for key, val in params.items(): - self[key] = val - # Map from script object names to their corresponding python classes _python_class_by_so_name = {} @@ -547,10 +484,15 @@ def script_interface_register(c): return c -cdef void init(MpiCallbacks & cb): +cdef void init(const shared_ptr[MpiCallbacks] & cb): cdef Factory[ObjectHandle] f initialize(& f) global _om _om = make_shared[ContextManager](cb, f) + + +cdef void deinit(): + global _om + _om.reset() diff --git a/src/python/espressomd/system.pxd b/src/python/espressomd/system.pxd index 75f23e6789..76e31d8c6e 100644 --- a/src/python/espressomd/system.pxd +++ b/src/python/espressomd/system.pxd @@ -24,7 +24,7 @@ from .utils cimport Vector3d cdef extern from "grid.hpp": void mpi_set_box_length(Vector3d length) except + void mpi_set_periodicity(bool x, bool y, bool z) - void rescale_boxl(int dir, double d_new) + void rescale_boxl(int dir, double d_new) except + cdef extern from "rotate_system.hpp": void mpi_rotate_system(double phi, double theta, double alpha) diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.pyx index 3bb08f381a..8148f38f86 100644 --- a/src/python/espressomd/system.pyx +++ b/src/python/espressomd/system.pyx @@ -202,10 +202,20 @@ cdef class System: self._active_virtual_sites_handle = virtual_sites.ActiveVirtualSitesHandle( implementation=virtual_sites.VirtualSitesOff()) + self._setup_atexit() + # lock class global _system_created _system_created = True + def _setup_atexit(self): + import atexit + + def session_shutdown(): + self.actors.clear() + + atexit.register(session_shutdown) + # __getstate__ and __setstate__ define the pickle interaction def __getstate__(self): checkpointable_properties = ["_box_geo", "integrator"] @@ -230,6 +240,7 @@ cdef class System: def __setstate__(self, params): for property_name in params.keys(): System.__setattr__(self, property_name, params[property_name]) + self._setup_atexit() property box_l: """ @@ -351,8 +362,8 @@ cdef class System: """ - if d_new < 0: - raise ValueError("No negative lengths") + if d_new <= 0.: + raise ValueError("Parameter 'd_new' must be > 0") if dir == "xyz": rescale_boxl(3, d_new) elif dir == "x" or dir == 0: @@ -364,6 +375,7 @@ cdef class System: else: raise ValueError( 'Usage: change_volume_and_rescale_particles(, [{ "x" | "y" | "z" | "xyz" }])') + utils.handle_errors("Exception while updating the box length") def volume(self): """Return box volume of the cuboid box. diff --git a/src/python/espressomd/thermostat.pyx b/src/python/espressomd/thermostat.pyx index 2af6fb25d1..0b00d7b823 100644 --- a/src/python/espressomd/thermostat.pyx +++ b/src/python/espressomd/thermostat.pyx @@ -407,9 +407,6 @@ cdef class Thermostat: The same applies to ``gamma_rotation``, which requires the feature ``ROTATION`` to work properly. But also accepts three floats if ``PARTICLE_ANISOTROPY`` is also compiled in. - act_on_virtual : :obj:`bool`, optional - If ``True`` the thermostat will act on virtual sites, default is - ``False``. seed : :obj:`int` Initial counter value (or seed) of the philox RNG. Required on first activation of the Brownian thermostat. @@ -417,6 +414,8 @@ cdef class Thermostat: """ + assert act_on_virtual is False, "Brownian dynamics virtual sites coupling was disabled in ESPResSo 4.2.2" + scalar_gamma_def = True scalar_gamma_rot_def = True IF PARTICLE_ANISOTROPY: diff --git a/src/python/espressomd/utils.pxd b/src/python/espressomd/utils.pxd index 746ff4f560..399c7f33d5 100644 --- a/src/python/espressomd/utils.pxd +++ b/src/python/espressomd/utils.pxd @@ -105,8 +105,8 @@ cdef extern from "utils/quaternion.hpp" namespace "Utils": cdef make_array_locked(Vector3d) cdef make_array_locked_vector(vector[Vector3d] v) -cdef Vector3d make_Vector3d(a) -cdef Vector3i make_Vector3i(a) +cdef Vector3d make_Vector3d(a) except * +cdef Vector3i make_Vector3i(a) except * cdef extern from "utils/Factory.hpp" namespace "Utils": cdef cppclass Factory[T]: diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx index 5c10cf40d4..7a28799d62 100644 --- a/src/python/espressomd/utils.pyx +++ b/src/python/espressomd/utils.pyx @@ -241,15 +241,17 @@ cdef make_array_locked_vector(vector[Vector3d] v): return array_locked(ret) -cdef Vector3d make_Vector3d(a): +cdef Vector3d make_Vector3d(a) except *: cdef Vector3d v + assert len(a) == 3 for i, ai in enumerate(a): v[i] = ai return v -cdef Vector3i make_Vector3i(a): +cdef Vector3i make_Vector3i(a) except *: cdef Vector3i v + assert len(a) == 3 for i, ai in enumerate(a): v[i] = ai return v diff --git a/src/python/espressomd/visualization.py b/src/python/espressomd/visualization.py index 5e92c27369..fb750e014c 100644 --- a/src/python/espressomd/visualization.py +++ b/src/python/espressomd/visualization.py @@ -1000,7 +1000,7 @@ def _draw_constraints(self): def _determine_radius(self, part_type): def radius_by_lj(part_type): ia = self.system.non_bonded_inter[part_type, part_type] - radius = 0.5 + radius = 0.0 if hasattr(ia, "lennard_jones"): radius = ia.lennard_jones.get_params()["sigma"] * 0.5 if radius == 0.0 and hasattr(ia, "wca"): diff --git a/src/python/object_in_fluid/oif_classes.py b/src/python/object_in_fluid/oif_classes.py index f471f18d45..7d5fa4e956 100644 --- a/src/python/object_in_fluid/oif_classes.py +++ b/src/python/object_in_fluid/oif_classes.py @@ -1147,7 +1147,7 @@ def elastic_forces( if (el_forces[0] == 1) or (el_forces[5] == 1) or ( f_metric[0] == 1) or (f_metric[5] == 1): # initialize list - stretching_forces_list = np.zeros((self.mesh.points, 3)) + stretching_forces_list = np.zeros((len(self.mesh.points), 3)) # calculation uses edges, but results are stored for nodes for e in self.mesh.edges: a_current_pos = e.A.get_pos() @@ -1171,7 +1171,7 @@ def elastic_forces( if (el_forces[1] == 1) or (el_forces[5] == 1) or ( f_metric[1] == 1) or (f_metric[5] == 1): # initialize list - bending_forces_list = np.zeros((self.mesh.points, 3)) + bending_forces_list = np.zeros((len(self.mesh.points), 3)) # calculation uses bending incidences, but results are stored for # nodes for angle in self.mesh.angles: @@ -1209,7 +1209,7 @@ def elastic_forces( if (el_forces[2] == 1) or (el_forces[5] == 1) or ( f_metric[2] == 1) or (f_metric[5] == 1): # initialize list - local_area_forces_list = np.zeros((self.mesh.points, 3)) + local_area_forces_list = np.zeros((len(self.mesh.points), 3)) # calculation uses triangles, but results are stored for nodes for t in self.mesh.triangles: a_current_pos = t.A.get_pos() @@ -1243,9 +1243,7 @@ def elastic_forces( if (el_forces[3] == 1) or (el_forces[5] == 1) or ( f_metric[3] == 1) or (f_metric[5] == 1): # initialize list - global_area_forces_list = [] - for _ in self.mesh.points: - global_area_forces_list.append([0.0, 0.0, 0.0]) + global_area_forces_list = np.zeros((len(self.mesh.points), 3)) # calculation uses triangles, but results are stored for nodes for t in self.mesh.triangles: a_current_pos = t.A.get_pos() @@ -1275,7 +1273,7 @@ def elastic_forces( if (el_forces[4] == 1) or (el_forces[5] == 1) or ( f_metric[4] == 1) or (f_metric[5] == 1): # initialize list - volume_forces_list = np.zeros((self.mesh.points, 3)) + volume_forces_list = np.zeros((len(self.mesh.points), 3)) # calculation uses triangles, but results are stored for nodes for t in self.mesh.triangles: a_current_pos = t.A.get_pos() @@ -1344,7 +1342,7 @@ def elastic_forces( # output vtk (folded) if vtk_file is not None: - if el_forces == (0, 0, 0, 0, 0, 0): + if sum(el_forces) == 0: raise Exception("OifCell: elastic_forces: The option elastic_forces was not used. " "Nothing to output to vtk file.") self.output_vtk_pos_folded(vtk_file) @@ -1405,8 +1403,7 @@ def elastic_forces( file_name=raw_data_file, data=elastic_forces_list) # return f_metric - if f_metric[0] + f_metric[1] + f_metric[2] + \ - f_metric[3] + f_metric[4] + f_metric[5] > 0: + if sum(f_metric) > 0: results = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] if f_metric[0] == 1: results[0] = ks_f_metric diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index a799571708..00916452bd 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -19,8 +19,8 @@ add_library( Espresso_script_interface SHARED - initialize.cpp ObjectHandle.cpp object_container_mpi_guard.cpp - GlobalContext.cpp ContextManager.cpp ParallelExceptionHandler.cpp) + initialize.cpp ObjectHandle.cpp GlobalContext.cpp ContextManager.cpp + ParallelExceptionHandler.cpp) add_library(Espresso::script_interface ALIAS Espresso_script_interface) add_subdirectory(accumulators) diff --git a/src/script_interface/ContextManager.cpp b/src/script_interface/ContextManager.cpp index 7bb3dfd37c..e7f20c3475 100644 --- a/src/script_interface/ContextManager.cpp +++ b/src/script_interface/ContextManager.cpp @@ -53,15 +53,16 @@ std::string ContextManager::serialize(const ObjectHandle *o) const { return Utils::pack(std::make_pair(policy(ctx), o->serialize())); } -ContextManager::ContextManager(Communication::MpiCallbacks &callbacks, - const Utils::Factory &factory) { +ContextManager::ContextManager( + std::shared_ptr const &callbacks, + const Utils::Factory &factory) { auto local_context = - std::make_shared(factory, callbacks.comm()); + std::make_shared(factory, callbacks->comm()); /* If there is only one node, we can treat all objects as local, and thus * never invoke any callback. */ m_global_context = - (callbacks.comm().size() > 1) + (callbacks->comm().size() > 1) ? std::make_shared(callbacks, local_context) : std::static_pointer_cast(local_context); diff --git a/src/script_interface/ContextManager.hpp b/src/script_interface/ContextManager.hpp index 7f5a098a59..47fa10a73b 100644 --- a/src/script_interface/ContextManager.hpp +++ b/src/script_interface/ContextManager.hpp @@ -67,7 +67,7 @@ class ContextManager { GLOBAL }; - ContextManager(Communication::MpiCallbacks &callbacks, + ContextManager(std::shared_ptr const &callbacks, const Utils::Factory &factory); /** diff --git a/src/script_interface/GlobalContext.hpp b/src/script_interface/GlobalContext.hpp index d4de6ebbc0..ede9133599 100644 --- a/src/script_interface/GlobalContext.hpp +++ b/src/script_interface/GlobalContext.hpp @@ -86,28 +86,28 @@ class GlobalContext : public Context { Communication::CallbackHandle cb_delete_handle; public: - GlobalContext(Communication::MpiCallbacks &callbacks, + GlobalContext(std::shared_ptr const &callbacks, std::shared_ptr node_local_context) : m_local_objects(), m_node_local_context(std::move(node_local_context)), - m_is_head_node(callbacks.comm().rank() == 0), + m_is_head_node(callbacks->comm().rank() == 0), // NOLINTNEXTLINE(bugprone-throw-keyword-missing) - m_parallel_exception_handler(callbacks.comm()), - cb_make_handle(&callbacks, + m_parallel_exception_handler(callbacks->comm()), + cb_make_handle(callbacks, [this](ObjectId id, const std::string &name, const PackedMap ¶meters) { make_handle(id, name, parameters); }), - cb_set_parameter(&callbacks, + cb_set_parameter(callbacks, [this](ObjectId id, std::string const &name, PackedVariant const &value) { set_parameter(id, name, value); }), - cb_call_method(&callbacks, + cb_call_method(callbacks, [this](ObjectId id, std::string const &name, PackedMap const &arguments) { call_method(id, name, arguments); }), - cb_delete_handle(&callbacks, + cb_delete_handle(callbacks, [this](ObjectId id) { delete_handle(id); }) {} private: diff --git a/src/script_interface/ObjectContainer.hpp b/src/script_interface/ObjectContainer.hpp new file mode 100644 index 0000000000..33b92089ff --- /dev/null +++ b/src/script_interface/ObjectContainer.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef SCRIPT_INTERFACE_OBJECT_CONTAINER_HPP +#define SCRIPT_INTERFACE_OBJECT_CONTAINER_HPP + +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { + +/** + * @brief Base class for containers whose @c BaseType might be a full + * specialization of @ref AutoParameters. + */ +template < + template class Container, typename ManagedType, + class BaseType, + class = std::enable_if_t::value>> +using ObjectContainer = std::conditional_t< + std::is_same::value, + AutoParameters, BaseType>, BaseType>; + +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/ObjectList.hpp b/src/script_interface/ObjectList.hpp index e43506afd1..72b7bb8f9b 100644 --- a/src/script_interface/ObjectList.hpp +++ b/src/script_interface/ObjectList.hpp @@ -22,9 +22,9 @@ #ifndef SCRIPT_INTERFACE_OBJECT_LIST_HPP #define SCRIPT_INTERFACE_OBJECT_LIST_HPP +#include "script_interface/ObjectContainer.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/get_value.hpp" -#include "script_interface/object_container_mpi_guard.hpp" #include @@ -35,20 +35,39 @@ #include namespace ScriptInterface { + /** * @brief Owning list of ObjectHandles * @tparam ManagedType Type of the managed objects, needs to be - * derived from ObjectHandle + * derived from @ref ObjectHandle */ -template < - typename ManagedType, class BaseType = ObjectHandle, - class = std::enable_if_t::value>> -class ObjectList : public BaseType { +template +class ObjectList : public ObjectContainer { +public: + using Base = ObjectContainer; + using Base::add_parameters; + private: + std::vector> m_elements; + virtual void add_in_core(const std::shared_ptr &obj_ptr) = 0; virtual void remove_in_core(const std::shared_ptr &obj_ptr) = 0; public: + ObjectList() { + add_parameters({ + {"_objects", AutoParameter::read_only, + [this]() { return make_vector_of_variants(m_elements); }}, + }); + } + + void do_construct(VariantMap const ¶ms) override { + m_elements = get_value_or(params, "_objects", {}); + for (auto const &object : m_elements) { + add_in_core(object); + } + } + /** * @brief Add an element to the list. * @@ -107,12 +126,7 @@ class ObjectList : public BaseType { } if (method == "get_elements") { - std::vector ret; - ret.reserve(m_elements.size()); - for (auto const &e : m_elements) - ret.emplace_back(e); - - return ret; + return make_vector_of_variants(m_elements); } if (method == "clear") { @@ -128,32 +142,8 @@ class ObjectList : public BaseType { return m_elements.empty(); } - return BaseType::do_call_method(method, parameters); + return Base::do_call_method(method, parameters); } - -private: - std::string get_internal_state() const override { - object_container_mpi_guard(BaseType::name(), m_elements.size()); - - std::vector object_states(m_elements.size()); - - boost::transform(m_elements, object_states.begin(), - [](auto const &e) { return e->serialize(); }); - - return Utils::pack(object_states); - } - - void set_internal_state(std::string const &state) override { - auto const object_states = Utils::unpack>(state); - - for (auto const &packed_object : object_states) { - auto o = std::dynamic_pointer_cast( - BaseType::deserialize(packed_object, *BaseType::context())); - add(std::move(o)); - } - } - - std::vector> m_elements; }; } // Namespace ScriptInterface #endif diff --git a/src/script_interface/ObjectMap.hpp b/src/script_interface/ObjectMap.hpp index 2556a4e198..3a7c6f4b94 100644 --- a/src/script_interface/ObjectMap.hpp +++ b/src/script_interface/ObjectMap.hpp @@ -22,11 +22,10 @@ #ifndef SCRIPT_INTERFACE_OBJECT_MAP_HPP #define SCRIPT_INTERFACE_OBJECT_MAP_HPP +#include "script_interface/ObjectContainer.hpp" #include "script_interface/ScriptInterface.hpp" +#include "script_interface/Variant.hpp" #include "script_interface/get_value.hpp" -#include "script_interface/object_container_mpi_guard.hpp" - -#include #include #include @@ -38,13 +37,18 @@ namespace ScriptInterface { /** * @brief Owning map of ObjectHandles * @tparam ManagedType Type of the managed objects, needs to be - * derived from ObjectHandle + * derived from @ref ObjectHandle */ -template < - typename ManagedType, class BaseType = ObjectHandle, class KeyType = int, - class = std::enable_if_t::value>> -class ObjectMap : public BaseType { +template +class ObjectMap : public ObjectContainer { +public: + using Base = ObjectContainer; + using Base::add_parameters; + private: + std::unordered_map> m_elements; + virtual KeyType insert_in_core(std::shared_ptr const &obj_ptr) = 0; virtual void insert_in_core(KeyType const &key, @@ -52,6 +56,20 @@ class ObjectMap : public BaseType { virtual void erase_in_core(KeyType const &key) = 0; public: + ObjectMap() { + add_parameters({ + {"_objects", AutoParameter::read_only, + [this]() { return make_unordered_map_of_variants(m_elements); }}, + }); + } + + void do_construct(VariantMap const ¶ms) override { + m_elements = get_value_or(params, "_objects", {}); + for (auto const &kv : m_elements) { + insert_in_core(kv.first, kv.second); + } + } + /** * @brief Add an element to the map. * @@ -110,7 +128,7 @@ class ObjectMap : public BaseType { get_value>(parameters.at("object")); if (parameters.count("key")) { - auto key = get_value(parameters.at("key")); + auto const key = get_key(parameters.at("key")); insert(key, obj_ptr); return none; } @@ -118,14 +136,13 @@ class ObjectMap : public BaseType { } if (method == "erase") { - auto key = get_value(parameters.at("key")); - + auto const key = get_key(parameters.at("key")); erase(key); return none; } if (method == "get") { - auto key = get_value(parameters.at("key")); + auto const key = get_key(parameters.at("key")); return Variant{m_elements.at(key)}; } @@ -155,39 +172,26 @@ class ObjectMap : public BaseType { } if (method == "contains") { - return m_elements.find(get_value(parameters.at("key"))) != - m_elements.end(); + return m_elements.find(get_key(parameters.at("key"))) != m_elements.end(); } - return BaseType::do_call_method(method, parameters); - } - -private: - std::string get_internal_state() const override { - object_container_mpi_guard(BaseType::name(), m_elements.size()); - - using packed_type = std::pair; - std::vector object_states(m_elements.size()); - - boost::transform(m_elements, object_states.begin(), [](auto const &kv) { - return std::make_pair(kv.first, kv.second->serialize()); - }); - - return Utils::pack(object_states); + return Base::do_call_method(method, parameters); } - void set_internal_state(std::string const &state) override { - using packed_type = std::pair; - auto const object_states = Utils::unpack>(state); - - for (auto const &packed_object : object_states) { - auto o = std::dynamic_pointer_cast( - BaseType::deserialize(packed_object.second, *BaseType::context())); - insert(packed_object.first, std::move(o)); + KeyType get_key(Variant const &key) const { + try { + return get_value(key); + } catch (...) { + using namespace detail::demangle; + auto const actual = simplify_symbol_variant(key); + auto const target = simplify_symbol(static_cast(nullptr)); + if (Base::context()->is_head_node()) { + throw std::invalid_argument("Key has to be of type '" + target + + "', got type '" + actual + "'"); + } + throw; } } - - std::unordered_map> m_elements; }; } // Namespace ScriptInterface #endif diff --git a/src/script_interface/auto_parameters/AutoParameters.hpp b/src/script_interface/auto_parameters/AutoParameters.hpp index f36849cd0b..15ddbe998c 100644 --- a/src/script_interface/auto_parameters/AutoParameters.hpp +++ b/src/script_interface/auto_parameters/AutoParameters.hpp @@ -115,7 +115,7 @@ class AutoParameters : public Base { if (m_parameters.count(p.name)) { m_parameters.erase(p.name); } - m_parameters.emplace(std::make_pair(p.name, std::move(p))); + m_parameters.emplace(p.name, std::move(p)); } } diff --git a/src/script_interface/electrostatics/CoulombScafacos.hpp b/src/script_interface/electrostatics/CoulombScafacos.hpp index 57c7ed059f..94b1089726 100644 --- a/src/script_interface/electrostatics/CoulombScafacos.hpp +++ b/src/script_interface/electrostatics/CoulombScafacos.hpp @@ -26,12 +26,16 @@ #include "Actor.hpp" +#include "core/MpiCallbacks.hpp" +#include "core/communication.hpp" #include "core/electrostatics/scafacos.hpp" #include "core/scafacos/ScafacosContextBase.hpp" #include "script_interface/get_value.hpp" #include "script_interface/scafacos/scafacos.hpp" +#include +#include #include #include #include @@ -42,6 +46,8 @@ namespace ScriptInterface { namespace Coulomb { class CoulombScafacos : public Actor { + std::shared_ptr m_mpi_env_lock; + public: CoulombScafacos() { add_parameters({ @@ -77,6 +83,11 @@ class CoulombScafacos : public Actor { }); } + ~CoulombScafacos() override { + m_actor.reset(); + m_mpi_env_lock.reset(); + } + void do_construct(VariantMap const ¶ms) override { auto const method_name = get_value(params, "method_name"); auto const param_list = params.at("method_params"); @@ -89,6 +100,8 @@ class CoulombScafacos : public Actor { actor()->set_prefactor(prefactor); }); set_charge_neutrality_tolerance(params); + // MPI communicator is needed to destroy the FFT plans + m_mpi_env_lock = ::Communication::mpiCallbacksHandle()->share_mpi_env(); } Variant do_call_method(std::string const &name, diff --git a/src/script_interface/h5md/h5md.hpp b/src/script_interface/h5md/h5md.hpp index 21e16bba28..82a0f2ef65 100644 --- a/src/script_interface/h5md/h5md.hpp +++ b/src/script_interface/h5md/h5md.hpp @@ -26,6 +26,8 @@ #ifdef H5MD +#include "core/MpiCallbacks.hpp" +#include "core/communication.hpp" #include "io/writer/h5md_core.hpp" #include "script_interface/ScriptInterface.hpp" @@ -66,8 +68,16 @@ class H5md : public AutoParameters { params, "file_path", "script_path", "fields", "mass_unit", "length_unit", "time_unit", "force_unit", "velocity_unit", "charge_unit"); + // MPI communicator is needed to close parallel file handles + m_mpi_env_lock = ::Communication::mpiCallbacksHandle()->share_mpi_env(); } + ~H5md() override { + m_h5md.reset(); + m_mpi_env_lock.reset(); + } + + std::shared_ptr m_mpi_env_lock; std::shared_ptr<::Writer::H5md::File> m_h5md; std::vector m_output_fields; }; diff --git a/src/script_interface/interactions/BondedInteractions.hpp b/src/script_interface/interactions/BondedInteractions.hpp index 047c13aaa7..924f49dba0 100644 --- a/src/script_interface/interactions/BondedInteractions.hpp +++ b/src/script_interface/interactions/BondedInteractions.hpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,16 @@ class BondedInteractions : public ObjectMap { using key_type = typename container_type::key_type; using mapped_type = typename container_type::mapped_type; + BondedInteractions() : ObjectMap::ObjectMap() { + add_parameters({ + {"_objects", AutoParameter::read_only, + []() { + // deactivate serialization (done at the Python level) + return make_unordered_map_of_variants(container_type{}); + }}, + }); + } + key_type insert_in_core(mapped_type const &obj_ptr) override { auto const key = ::bonded_ia_params.insert(obj_ptr->bonded_ia()); m_bonds[key] = std::move(obj_ptr); @@ -64,6 +75,7 @@ class BondedInteractions : public ObjectMap { mpi_update_cell_system_ia_range_local(); } +protected: Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { if (name == "get_size") { @@ -78,12 +90,12 @@ class BondedInteractions : public ObjectMap { } if (name == "has_bond") { - auto const bond_id = get_value(params, "bond_id"); + auto const bond_id = get_key(params.at("bond_id")); return {m_bonds.count(bond_id) != 0}; } if (name == "get_bond") { - auto const bond_id = get_value(params, "bond_id"); + auto const bond_id = get_key(params.at("bond_id")); // core and script interface must agree assert(m_bonds.count(bond_id) == ::bonded_ia_params.count(bond_id)); if (not context()->is_head_node()) @@ -100,9 +112,6 @@ class BondedInteractions : public ObjectMap { } private: - // disable serialization: pickling done by the python interface - std::string get_internal_state() const override { return {}; } - void set_internal_state(std::string const &state) override {} container_type m_bonds; }; } // namespace Interactions diff --git a/src/script_interface/lees_edwards/LeesEdwards.hpp b/src/script_interface/lees_edwards/LeesEdwards.hpp index 76a1688dad..01bf9b3f96 100644 --- a/src/script_interface/lees_edwards/LeesEdwards.hpp +++ b/src/script_interface/lees_edwards/LeesEdwards.hpp @@ -22,6 +22,7 @@ #include "Protocol.hpp" #include "core/grid.hpp" +#include "core/lees_edwards/LeesEdwardsBC.hpp" #include "core/lees_edwards/lees_edwards.hpp" #include "script_interface/ScriptInterface.hpp" @@ -35,7 +36,7 @@ namespace LeesEdwards { class LeesEdwards : public AutoParameters { std::shared_ptr m_protocol; - LeesEdwardsBC const &m_lebc = box_geo.lees_edwards_bc(); + LeesEdwardsBC const &m_lebc = ::box_geo.lees_edwards_bc(); public: LeesEdwards() : m_protocol{nullptr} { @@ -44,13 +45,14 @@ class LeesEdwards : public AutoParameters { [this](Variant const &value) { if (is_none(value)) { m_protocol = nullptr; - box_geo.set_lees_edwards_bc(LeesEdwardsBC{0., 0., -1, -1}); + ::box_geo.set_lees_edwards_bc(LeesEdwardsBC{}); ::LeesEdwards::unset_protocol(); return; } context()->parallel_try_catch([this]() { - if (m_lebc.shear_direction == -1 or - m_lebc.shear_plane_normal == -1) { + auto constexpr invalid_dir = LeesEdwardsBC::invalid_dir; + if (m_lebc.shear_direction == invalid_dir or + m_lebc.shear_plane_normal == invalid_dir) { throw std::runtime_error( "Parameters 'shear_plane_normal' and 'shear_direction' " "must be initialized together with 'protocol' on first " @@ -94,7 +96,7 @@ class LeesEdwards : public AutoParameters { "'shear_plane_normal' must differ"); } // update box geometry and cell structure - box_geo.set_lees_edwards_bc( + ::box_geo.set_lees_edwards_bc( LeesEdwardsBC{0., 0., shear_direction, shear_plane_normal}); ::LeesEdwards::set_protocol(m_protocol->protocol()); }); @@ -109,28 +111,28 @@ class LeesEdwards : public AutoParameters { } private: - int get_shear_axis(VariantMap const ¶ms, std::string name) { + unsigned int get_shear_axis(VariantMap const ¶ms, std::string name) { auto const value = get_value(params, name); if (value == "x") { - return 0; + return 0u; } if (value == "y") { - return 1; + return 1u; } if (value == "z") { - return 2; + return 2u; } throw std::invalid_argument("Parameter '" + name + "' is invalid"); } - Variant get_shear_name(int axis) { - if (axis == 0) { + Variant get_shear_name(unsigned int axis) { + if (axis == 0u) { return {std::string("x")}; } - if (axis == 1) { + if (axis == 1u) { return {std::string("y")}; } - if (axis == 2) { + if (axis == 2u) { return {std::string("z")}; } return {none}; diff --git a/src/script_interface/magnetostatics/DipolarScafacos.hpp b/src/script_interface/magnetostatics/DipolarScafacos.hpp index ef53205ef8..61c99cd559 100644 --- a/src/script_interface/magnetostatics/DipolarScafacos.hpp +++ b/src/script_interface/magnetostatics/DipolarScafacos.hpp @@ -26,11 +26,15 @@ #include "Actor.hpp" +#include "core/MpiCallbacks.hpp" +#include "core/communication.hpp" #include "core/magnetostatics/scafacos.hpp" #include "core/scafacos/ScafacosContextBase.hpp" #include "script_interface/scafacos/scafacos.hpp" +#include +#include #include #include @@ -38,6 +42,7 @@ namespace ScriptInterface { namespace Dipoles { class DipolarScafacos : public Actor { + std::shared_ptr m_mpi_env_lock; public: DipolarScafacos() { @@ -51,6 +56,11 @@ class DipolarScafacos : public Actor { }); } + ~DipolarScafacos() override { + m_actor.reset(); + m_mpi_env_lock.reset(); + } + void do_construct(VariantMap const ¶ms) override { auto const method_name = get_value(params, "method_name"); auto const param_list = params.at("method_params"); @@ -65,6 +75,8 @@ class DipolarScafacos : public Actor { m_actor = make_dipolar_scafacos(method_name, method_params); actor()->prefactor = prefactor; }); + // MPI communicator is needed to destroy the FFT plans + m_mpi_env_lock = ::Communication::mpiCallbacksHandle()->share_mpi_env(); } Variant do_call_method(std::string const &name, diff --git a/src/script_interface/object_container_mpi_guard.cpp b/src/script_interface/object_container_mpi_guard.cpp deleted file mode 100644 index 4f1e680e28..0000000000 --- a/src/script_interface/object_container_mpi_guard.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2021-2022 The ESPResSo project - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "script_interface/object_container_mpi_guard.hpp" - -#include "core/communication.hpp" - -#include - -#include -#include -#include - -void object_container_mpi_guard(boost::string_ref const &name, - std::size_t n_elements) { - if (comm_cart.size() > 1 and n_elements) { - std::stringstream error_msg; - error_msg << "Non-empty object containers do not support checkpointing in " - << "MPI environments. Container " << name << " contains " - << n_elements << " elements."; - throw std::runtime_error(error_msg.str()); - } -} diff --git a/src/script_interface/object_container_mpi_guard.hpp b/src/script_interface/object_container_mpi_guard.hpp deleted file mode 100644 index 90eededb14..0000000000 --- a/src/script_interface/object_container_mpi_guard.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2021-2022 The ESPResSo project - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef ESPRESSO_OBJECT_CONTAINER_MPI_GUARD_HPP -#define ESPRESSO_OBJECT_CONTAINER_MPI_GUARD_HPP - -#include - -#include - -/** - * @brief Prevent object container serialization. - * - * The @ref ScriptInterface::ObjectHandle framework doesn't support - * recursive deserialization. When an object container such as - * @ref ScriptInterface::ObjectList is deserialized, the contained - * objects are deserialized on the head node only, which leads to - * silent bugs in simulations. - * - * This function needs to be called from an object container - * get_internal_state() method to throw a runtime error - * when the container is not empty and the MPI world size is - * greater than 1. - * - * @param name Name of the object container - * @param n_elements Number of elements in the container - */ -void object_container_mpi_guard(boost::string_ref const &name, - std::size_t n_elements); - -#endif diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt index c78015a7b4..bfc7fe9bc2 100644 --- a/src/script_interface/tests/CMakeLists.txt +++ b/src/script_interface/tests/CMakeLists.txt @@ -45,9 +45,6 @@ unit_test(NAME ObjectList_test SRC ObjectList_test.cpp DEPENDS Espresso::script_interface Espresso::core Boost::mpi) unit_test(NAME ObjectMap_test SRC ObjectMap_test.cpp DEPENDS Espresso::script_interface Espresso::core Boost::mpi) -unit_test(NAME serialization_mpi_guard_test SRC - serialization_mpi_guard_test.cpp DEPENDS Espresso::script_interface - Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME Accumulators_test SRC Accumulators_test.cpp DEPENDS Espresso::script_interface Espresso::core) unit_test(NAME Constraints_test SRC Constraints_test.cpp DEPENDS diff --git a/src/script_interface/tests/GlobalContext_test.cpp b/src/script_interface/tests/GlobalContext_test.cpp index b5556b2d18..da0401ad41 100644 --- a/src/script_interface/tests/GlobalContext_test.cpp +++ b/src/script_interface/tests/GlobalContext_test.cpp @@ -26,12 +26,15 @@ #include #include +#include #include #include #include #include +static std::weak_ptr mpi_env; + namespace si = ScriptInterface; struct Dummy : si::ObjectHandle { @@ -54,18 +57,18 @@ struct Dummy : si::ObjectHandle { } }; -auto make_global_context(Communication::MpiCallbacks &cb) { +auto make_global_context(std::shared_ptr &cb) { Utils::Factory factory; factory.register_new("Dummy"); - boost::mpi::communicator comm; return std::make_shared( - cb, std::make_shared(factory, comm)); + cb, std::make_shared(factory, cb->comm())); } BOOST_AUTO_TEST_CASE(GlobalContext_make_shared) { boost::mpi::communicator world; - Communication::MpiCallbacks cb{world}; + auto cb = + std::make_shared(world, ::mpi_env.lock()); auto ctx = make_global_context(cb); if (world.rank() == 0) { @@ -74,13 +77,14 @@ BOOST_AUTO_TEST_CASE(GlobalContext_make_shared) { BOOST_CHECK_EQUAL(res->context(), ctx.get()); BOOST_CHECK_EQUAL(ctx->name(res.get()), "Dummy"); } else { - cb.loop(); + cb->loop(); } } BOOST_AUTO_TEST_CASE(GlobalContext_serialization) { boost::mpi::communicator world; - Communication::MpiCallbacks cb{world}; + auto cb = + std::make_shared(world, ::mpi_env.lock()); auto ctx = make_global_context(cb); if (world.rank() == 0) { @@ -108,12 +112,13 @@ BOOST_AUTO_TEST_CASE(GlobalContext_serialization) { BOOST_REQUIRE(d3); BOOST_CHECK_EQUAL(boost::get(d3->get_parameter("id")), 3); } else { - cb.loop(); + cb->loop(); } } int main(int argc, char **argv) { - boost::mpi::environment mpi_env(argc, argv); + auto const mpi_env = std::make_shared(argc, argv); + ::mpi_env = mpi_env; return boost::unit_test::unit_test_main(init_unit_test, argc, argv); } diff --git a/src/script_interface/tests/ParallelExceptionHandler_test.cpp b/src/script_interface/tests/ParallelExceptionHandler_test.cpp index c78c11c0cf..8a475c6a3c 100644 --- a/src/script_interface/tests/ParallelExceptionHandler_test.cpp +++ b/src/script_interface/tests/ParallelExceptionHandler_test.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -52,6 +53,8 @@ namespace utf = boost::unit_test; +static std::weak_ptr mpi_env; + namespace Testing { struct Error : public std::exception {}; struct Warning : public std::exception {}; @@ -68,7 +71,8 @@ struct if_parallel_test { BOOST_TEST_DECORATOR(*utf::precondition(if_parallel_test())) BOOST_AUTO_TEST_CASE(parallel_exceptions) { boost::mpi::communicator world; - Communication::MpiCallbacks callbacks{world}; + auto callbacks = + std::make_shared(world, ::mpi_env.lock()); ErrorHandling::init_error_handling(callbacks); auto handler = ScriptInterface::ParallelExceptionHandler{world}; @@ -130,10 +134,14 @@ BOOST_AUTO_TEST_CASE(parallel_exceptions) { // runtime error messages are printed to stderr and cleared BOOST_CHECK_EQUAL(check_runtime_errors_local(), 0); } + if (world.rank() != 0) { + callbacks->loop(); + } } int main(int argc, char **argv) { - boost::mpi::environment mpi_env(argc, argv); + auto const mpi_env = std::make_shared(argc, argv); + ::mpi_env = mpi_env; return boost::unit_test::unit_test_main(init_unit_test, argc, argv); } diff --git a/src/script_interface/tests/serialization_mpi_guard_test.cpp b/src/script_interface/tests/serialization_mpi_guard_test.cpp deleted file mode 100644 index 88fea91e85..0000000000 --- a/src/script_interface/tests/serialization_mpi_guard_test.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2022 The ESPResSo project - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#define BOOST_TEST_NO_MAIN -#define BOOST_TEST_MODULE Object container MPI guard test -#define BOOST_TEST_DYN_LINK -#include - -#include "script_interface/ObjectList.hpp" - -#include - -#include -#include -#include -#include -#include - -using ScriptInterface::ObjectHandle; - -namespace Testing { -struct ObjectContainer : ScriptInterface::ObjectList { - std::vector> objects; - -private: - void add_in_core(std::shared_ptr const &obj_ptr) override { - objects.push_back(obj_ptr); - } - void remove_in_core(std::shared_ptr const &obj_ptr) override { - objects.erase(std::remove(objects.begin(), objects.end(), obj_ptr), - objects.end()); - } -}; -} // namespace Testing - -BOOST_AUTO_TEST_CASE(parallel_exception) { - boost::mpi::communicator world; - auto const obj_ptr = std::make_shared(); - auto const predicate = [](std::exception const &ex) { - std::string message = - "Non-empty object containers do not support checkpointing " - "in MPI environments. Container contains 1 elements."; - return ex.what() == message; - }; - - Testing::ObjectContainer list; - BOOST_CHECK_NO_THROW(list.serialize()); - - list.add(obj_ptr); - if (world.size() > 1) { - BOOST_CHECK_EXCEPTION(list.serialize(), std::runtime_error, predicate); - } - - list.remove(obj_ptr); - BOOST_CHECK_NO_THROW(list.serialize()); -} - -int main(int argc, char **argv) { - boost::mpi::environment mpi_env(argc, argv); - - return boost::unit_test::unit_test_main(init_unit_test, argc, argv); -} diff --git a/src/shapes/src/Ellipsoid.cpp b/src/shapes/src/Ellipsoid.cpp index dbad6617f1..128f0fd85f 100644 --- a/src/shapes/src/Ellipsoid.cpp +++ b/src/shapes/src/Ellipsoid.cpp @@ -52,9 +52,9 @@ void Ellipsoid::calculate_dist(const Utils::Vector3d &pos, double &dist, } /* calculate dist and vec */ - for (int i = 0; i < 3; i++) { - vec[i] = (ppos_e[i] - Utils::sqr(m_semiaxes[i]) * ppos_e[i] / - (l + Utils::sqr(m_semiaxes[i]))); + for (unsigned int i = 0; i < 3; i++) { + auto const semi_sq = Utils::sqr(m_semiaxes[i]); + vec[i] = (ppos_e[i] - semi_sq * ppos_e[i] / (l + semi_sq)); } dist = distance_prefactor * m_direction * vec.norm(); @@ -67,7 +67,7 @@ bool Ellipsoid::inside_ellipsoid(const Utils::Vector3d &ppos) const { double Ellipsoid::newton_term(const Utils::Vector3d &ppos, const double &l) const { Utils::Vector3d axpos, lax, lax2; - for (int i = 0; i < 3; i++) { + for (unsigned int i = 0; i < 3; i++) { axpos[i] = Utils::sqr(m_semiaxes[i]) * Utils::sqr(ppos[i]); lax[i] = l + Utils::sqr(m_semiaxes[i]); lax2[i] = Utils::sqr(lax[i]); diff --git a/src/utils/include/utils/Vector.hpp b/src/utils/include/utils/Vector.hpp index dfb212b605..d9828b874a 100644 --- a/src/utils/include/utils/Vector.hpp +++ b/src/utils/include/utils/Vector.hpp @@ -140,7 +140,7 @@ template class Vector : public Array { Vector &normalize() { auto const l = norm(); if (l > T(0)) { - for (int i = 0; i < N; i++) + for (std::size_t i = 0; i < N; i++) this->operator[](i) /= l; } @@ -188,7 +188,7 @@ Vector &binary_op_assign(Vector &a, Vector const &b, Op op) { template constexpr bool all_of(Vector const &a, Vector const &b, Op op) { - for (int i = 0; i < a.size(); i++) { + for (unsigned int i = 0; i < N; i++) { /* Short circuit */ if (!static_cast(op(a[i], b[i]))) { return false; @@ -448,7 +448,7 @@ auto hadamard_division(T const &a, U const &b) { return a / b; } -template Vector unit_vector(int i) { +template Vector unit_vector(unsigned int i) { if (i == 0) return {T{1}, T{0}, T{0}}; if (i == 1) diff --git a/src/utils/include/utils/integral_parameter.hpp b/src/utils/include/utils/integral_parameter.hpp index 06f174306d..326e91b17e 100644 --- a/src/utils/include/utils/integral_parameter.hpp +++ b/src/utils/include/utils/integral_parameter.hpp @@ -27,22 +27,20 @@ namespace Utils { namespace detail { -template