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 83f4866e6d..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:
@@ -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/doc/sphinx/installation.rst b/doc/sphinx/installation.rst
index f4ff1d6850..a7e3bc84b6 100644
--- a/doc/sphinx/installation.rst
+++ b/doc/sphinx/installation.rst
@@ -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
@@ -247,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 <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`__ to install Ubuntu
-* start Ubuntu (or open an Ubuntu tab in `Windows Terminal <https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701>`__)
+* follow `these instructions <https://learn.microsoft.com/en-us/windows/wsl/install>`__ to install Ubuntu
+* start Ubuntu (or open an Ubuntu tab in `Windows Terminal <https://apps.microsoft.com/detail/9n0dx20hk701?hl=en-us&gl=US>`__)
 * 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 <https://docs.nvidia.com/cuda/wsl-user-guide/index.html#ch03a-setting-up-cuda>`__
+  use of |es|'s GPU acceleration, follow `these instructions <https://docs.nvidia.com/cuda/wsl-user-guide/index.html>`__
   to set up CUDA.
 * follow the instructions for :ref:`Installing requirements on Ubuntu Linux`
 
diff --git a/doc/tutorials/error_analysis/error_analysis_part2.ipynb b/doc/tutorials/error_analysis/error_analysis_part2.ipynb
index 29c64d029c..cbce23701c 100644
--- a/doc/tutorials/error_analysis/error_analysis_part2.ipynb
+++ b/doc/tutorials/error_analysis/error_analysis_part2.ipynb
@@ -205,7 +205,8 @@
     "plt.plot(autocov)\n",
     "plt.xlabel(\"lag time $j$\")\n",
     "plt.ylabel(r\"$\\hat{K}^{XX}_j$\")\n",
-    "plt.show()"
+    "plt.show()\n",
+    "```"
    ]
   },
   {
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 <tt>std::erfc()</tt> in P3M and Ewald summation.
  */
 #ifndef USE_ERFC_APPROXIMATION
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/python/espressomd/CMakeLists.txt b/src/python/espressomd/CMakeLists.txt
index 2a18f5f6ca..a508e799e3 100644
--- a/src/python/espressomd/CMakeLists.txt
+++ b/src/python/espressomd/CMakeLists.txt
@@ -73,6 +73,10 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL
     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} $<$<BOOL:${WARNINGS_ARE_ERRORS}>:--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/testsuite/python/sigint_child.py b/testsuite/python/sigint_child.py
index ed86ecec3c..efd63e36b7 100644
--- a/testsuite/python/sigint_child.py
+++ b/testsuite/python/sigint_child.py
@@ -19,6 +19,8 @@
 import numpy as np
 import espressomd
 
+np.random.seed(42)
+
 system = espressomd.System(box_l=[100, 100, 100])
 system.time_step = 0.01
 system.cell_system.skin = 0.1